From f535fd3b9018b602f7efc66c53ef55a31210b55c Mon Sep 17 00:00:00 2001 From: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Date: Tue, 5 Sep 2023 19:41:24 +0530 Subject: [PATCH 01/50] Enhance UI of Import External Results page (#6163) * enhance ui * place cancel button to righ * fix width --- .../ExternalResult/ExternalResultUpload.tsx | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Components/ExternalResult/ExternalResultUpload.tsx b/src/Components/ExternalResult/ExternalResultUpload.tsx index 4d517f1a442..5f39cb213a4 100644 --- a/src/Components/ExternalResult/ExternalResultUpload.tsx +++ b/src/Components/ExternalResult/ExternalResultUpload.tsx @@ -8,6 +8,8 @@ import { externalResultUploadCsv } from "../../Redux/actions"; import * as Notification from "../../Utils/Notifications.js"; const PageTitle = lazy(() => import("../Common/PageTitle")); import { useTranslation } from "react-i18next"; +import { Cancel, Submit } from "../Common/components/ButtonV2"; +import useAppHistory from "../../Common/hooks/useAppHistory"; export default function ExternalResultUpload() { const { sample_format_external_result_import } = useConfig(); @@ -20,6 +22,7 @@ export default function ExternalResultUpload() { setCsvData(data); }; const { t } = useTranslation(); + const { goBack } = useAppHistory(); const papaparseOptions = { header: true, @@ -67,11 +70,11 @@ export default function ExternalResultUpload() { backUrl="/external_results" className="mt-4" /> -
-
+
+
-
+
-
- + disabled={loading} + label={t("save")} + data-testid="submit-button" + />
From b0c61dc0044bf80a6a13277f92a6eec59973a644 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Tue, 5 Sep 2023 14:32:01 +0000 Subject: [PATCH 02/50] Medicine: Adds `type` filter for medibase search (#6186) * add type filter for medibase * Consultation: Verified By Doctor as Dropdown * add missing model changes * Revert "add missing model changes" This reverts commit 149592e98a610ee0661076f05df5bfeaed8b25bf. * Revert "Consultation: Verified By Doctor as Dropdown" This reverts commit 4113172c929c99e96b7dfee74f597cc4fd13fb61. --- src/CAREUI/interactive/Switch.tsx | 39 +++++++++++++++++++ .../MedibaseAutocompleteFormField.tsx | 30 +++++++++++++- src/Redux/actions.tsx | 8 +++- 3 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 src/CAREUI/interactive/Switch.tsx diff --git a/src/CAREUI/interactive/Switch.tsx b/src/CAREUI/interactive/Switch.tsx new file mode 100644 index 00000000000..3e18795b034 --- /dev/null +++ b/src/CAREUI/interactive/Switch.tsx @@ -0,0 +1,39 @@ +import { classNames } from "../../Utils/utils"; + +interface Props { + tabs: Record; + selected: T; + onChange: (tab: T) => void; + size?: "sm" | "md" | "lg"; +} + +export default function Switch({ + size = "sm", + ...props +}: Props) { + return ( +
    + {Object.keys(props.tabs).map((tab) => { + return ( +
  • props.onChange(tab as T)} + > + {props.tabs[tab as T]} +
  • + ); + })} +
+ ); +} diff --git a/src/Components/Medicine/MedibaseAutocompleteFormField.tsx b/src/Components/Medicine/MedibaseAutocompleteFormField.tsx index 473bb3a2598..337546691f9 100644 --- a/src/Components/Medicine/MedibaseAutocompleteFormField.tsx +++ b/src/Components/Medicine/MedibaseAutocompleteFormField.tsx @@ -1,3 +1,5 @@ +import { useEffect, useState } from "react"; +import Switch from "../../CAREUI/interactive/Switch"; import { useAsyncOptions } from "../../Common/hooks/useAsyncOptions"; import { listMedibaseMedicines } from "../../Redux/actions"; import { Autocomplete } from "../Form/FormFields/Autocomplete"; @@ -15,8 +17,32 @@ export default function MedibaseAutocompleteFormField( const { isLoading, options, fetchOptions } = useAsyncOptions("id"); + const [query, setQuery] = useState(""); + const [type, setType] = useState(); + + useEffect(() => { + fetchOptions(listMedibaseMedicines(query, type)); + }, [query, type]); + return ( - + { + setType(type === "all" ? undefined : type); + }} + /> + ), + }} + > ) } - onQuery={(query) => fetchOptions(listMedibaseMedicines(query))} + onQuery={setQuery} isLoading={isLoading} /> diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index 0ea1270f741..e3212685d2f 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -1,5 +1,6 @@ import { HCXClaimModel, HCXPolicyModel } from "../Components/HCX/models"; import { + MedibaseMedicine, MedicineAdministrationRecord, Prescription, } from "../Components/Medicine/models"; @@ -803,8 +804,11 @@ export const listICD11Diagnosis = (params: object, key: string) => { return fireRequest("listICD11Diagnosis", [], params, null, key); }; // Medibase -export const listMedibaseMedicines = (query: string) => { - return fireRequest("listMedibaseMedicines", [], { query }); +export const listMedibaseMedicines = ( + query: string, + type?: MedibaseMedicine["type"] +) => { + return fireRequest("listMedibaseMedicines", [], { query, type }); }; // Resource From 14f6ff1e239c3bc8f17a945dc36eb1bb0a29b774 Mon Sep 17 00:00:00 2001 From: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Date: Tue, 5 Sep 2023 20:06:17 +0530 Subject: [PATCH 03/50] remove extra space (#6215) --- src/Components/Facility/TriageForm.tsx | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/Components/Facility/TriageForm.tsx b/src/Components/Facility/TriageForm.tsx index a627fda1bbe..3c8e6b1497b 100644 --- a/src/Components/Facility/TriageForm.tsx +++ b/src/Components/Facility/TriageForm.tsx @@ -283,20 +283,20 @@ export const TriageForm = (props: triageFormProps) => { handleSubmit(); }} > -
- -
+
+ +
{ />
-
+
goBack()} />
From 1631e7953c4f64400b7e357b11c772af3836a17b Mon Sep 17 00:00:00 2001 From: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Date: Tue, 5 Sep 2023 20:07:13 +0530 Subject: [PATCH 04/50] remove extra loader and add success notification (#6210) --- src/Components/Users/SkillsSlideOver.tsx | 47 ++++++++++++------------ 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/src/Components/Users/SkillsSlideOver.tsx b/src/Components/Users/SkillsSlideOver.tsx index cf43b738181..343ea9062cc 100644 --- a/src/Components/Users/SkillsSlideOver.tsx +++ b/src/Components/Users/SkillsSlideOver.tsx @@ -55,6 +55,10 @@ export default ({ show, setShow, username }: IProps) => { Notification.Error({ msg: "Error while adding skill", }); + } else { + Notification.Success({ + msg: "Skill added successfully", + }); } setSelectedSkill(null); setIsLoading(false); @@ -110,21 +114,19 @@ export default ({ show, setShow, username }: IProps) => { >
-
- - {isLoading ? ( - - ) : ( + {!isLoading && ( +
+ addSkill(username, selectedSkill)} @@ -132,14 +134,13 @@ export default ({ show, setShow, username }: IProps) => { > {t("add")} - )} - {!authorizeForAddSkill && ( - - {t("contact_your_admin_to_add_skills")} - - )} -
- {/* While loading skills, we display an additional circular progress to show we are fetching the information*/} + {!authorizeForAddSkill && ( + + {t("contact_your_admin_to_add_skills")} + + )} +
+ )} {isLoading ? (
From 9d9bb5e5a182934507b4f2a0f5d22f2d01658464 Mon Sep 17 00:00:00 2001 From: Mustafa Azad <97380192+mustafaazad03@users.noreply.github.com> Date: Tue, 5 Sep 2023 20:17:41 +0530 Subject: [PATCH 05/50] Bug Fixed : Default filters on patients page loads (#6189) * URL Bug fixed * filters issue fixed * default filters apply on patient page : Bug Fixed * Update src/Components/Patient/ManagePatients.tsx Co-authored-by: Rithvik Nishad * Update src/Components/Patient/ManagePatients.tsx Co-authored-by: Rithvik Nishad --------- Co-authored-by: Rithvik Nishad --- src/Components/Patient/ManagePatients.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx index 677e3725289..b29c112c5a1 100644 --- a/src/Components/Patient/ManagePatients.tsx +++ b/src/Components/Patient/ManagePatients.tsx @@ -121,7 +121,7 @@ export const PatientManager = () => { if (phone_number === "+91" || phone_number === "") { setPhoneNumberError(""); - updateQuery({ phone_number: "" }); + qParams.phone_number && updateQuery({ phone_number: null }); return; } @@ -138,7 +138,8 @@ export const PatientManager = () => { if (emergency_phone_number === "+91" || emergency_phone_number === "") { setEmergencyPhoneNumberError(""); - updateQuery({ emergency_phone_number: "" }); + qParams.emergency_phone_number && + updateQuery({ emergency_phone_number: null }); return; } From bbb07b773f9e6bcddfd1589b20069f0fb3ef32e3 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Tue, 5 Sep 2023 15:12:47 +0000 Subject: [PATCH 06/50] Support for direnv and use `CARE_API` from env in vite config (#6201) * optionally use `careapi` from env * update cypress ci * bring back quotes * fix cypress env properly * support for `direnv` --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- .envrc | 1 + .github/workflows/cypress.yaml | 4 +--- vite.config.ts | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 00000000000..fc7d890f90a --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +test -f .env.local && dotenv .env.local \ No newline at end of file diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index e98f5b01e7d..4f23fbe4937 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -35,9 +35,6 @@ jobs: - name: Check care is up โ™ป run: curl -o /dev/null -s -w "%{http_code}\n" http://localhost:9000 - - name: Change api proxy url ๐Ÿ“ - run: 'sed --in-place "s^target: .*,^target: \"http://localhost:9000\",^g" vite.config.ts' - - name: Install dependencies ๐Ÿ“ฆ run: npm install @@ -55,6 +52,7 @@ jobs: browser: chrome record: true env: + CARE_API: http://localhost:9000 CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max_old_space_size=4096 diff --git a/vite.config.ts b/vite.config.ts index 1c88d8e0022..7aafe6e1d5c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -84,7 +84,7 @@ export default defineConfig({ port: 4000, proxy: { "/api": { - target: "https://careapi.ohc.network", + target: process.env.CARE_API ?? "https://careapi.ohc.network", changeOrigin: true, }, }, @@ -93,7 +93,7 @@ export default defineConfig({ port: 4000, proxy: { "/api": { - target: "https://careapi.ohc.network", + target: process.env.CARE_API ?? "https://careapi.ohc.network", changeOrigin: true, }, }, From 539beecc51200417059258faf1c7c5b8e2efb213 Mon Sep 17 00:00:00 2001 From: Suprabath <34211797+suprabathk@users.noreply.github.com> Date: Wed, 6 Sep 2023 08:38:58 +0530 Subject: [PATCH 07/50] Added nav link for bed status in consultation form (#6170) --- src/Components/Facility/ConsultationForm.tsx | 22 +++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index ed7fd7b57b6..c71c70b162c 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -198,7 +198,8 @@ const consultationFormReducer = (state = initialState, action: FormAction) => { type ConsultationFormSection = | "Consultation Details" | "Diagnosis" - | "Treatment Plan"; + | "Treatment Plan" + | "Bed Status"; export const ConsultationForm = (props: any) => { const { goBack } = useAppHistory(); @@ -223,6 +224,7 @@ export const ConsultationForm = (props: any) => { const [consultationDetailsVisible, consultationDetailsRef] = useVisibility(); const [diagnosisVisible, diagnosisRef] = useVisibility(-300); const [treatmentPlanVisible, treatmentPlanRef] = useVisibility(-300); + const [bedStatusVisible, bedStatusRef] = useVisibility(-300); const [disabledFields, setDisabledFields] = useState([]); const sections = { @@ -241,6 +243,11 @@ export const ConsultationForm = (props: any) => { visible: treatmentPlanVisible, ref: treatmentPlanRef, }, + "Bed Status": { + iconClass: "care-l-bed", + visible: bedStatusVisible, + ref: bedStatusRef, + }, }; useEffect(() => { @@ -248,9 +255,15 @@ export const ConsultationForm = (props: any) => { if (consultationDetailsVisible) return "Consultation Details"; if (diagnosisVisible) return "Diagnosis"; if (treatmentPlanVisible) return "Treatment Plan"; + if (bedStatusVisible) return "Bed Status"; return prev; }); - }, [consultationDetailsVisible, diagnosisVisible, treatmentPlanVisible]); + }, [ + consultationDetailsVisible, + diagnosisVisible, + treatmentPlanVisible, + bedStatusVisible, + ]); useEffect(() => { async function fetchPatientName() { @@ -817,6 +830,9 @@ export const ConsultationForm = (props: any) => { if (state.form.consultation_status === 1) { return null; } + if (!isUpdate && sectionTitle === "Bed Status") { + return null; + } const isCurrent = currentSection === sectionTitle; const section = sections[sectionTitle as ConsultationFormSection]; return ( @@ -1306,7 +1322,7 @@ export const ConsultationForm = (props: any) => { {isUpdate && ( <>
-

Update Bed

+ {sectionTitle("Bed Status")} Date: Wed, 6 Sep 2023 03:10:03 +0000 Subject: [PATCH 08/50] Medicine Administrations: Adds `administered_date` input (#6206) * Add input: `administered_date` to Administer Dialog * add administered_date for bulk administer too * fix cypress --- cypress/e2e/patient_spec/patient_crud.cy.ts | 2 +- .../Form/FormFields/CheckBoxFormField.tsx | 1 + .../Medicine/AdministerMedicine.tsx | 61 ++++++++++--- .../Medicine/MedicineAdministration.tsx | 88 +++++++++++++++---- .../PrescriptionAdministrationsTable.tsx | 2 +- src/Components/Medicine/models.ts | 2 +- 6 files changed, 125 insertions(+), 31 deletions(-) diff --git a/cypress/e2e/patient_spec/patient_crud.cy.ts b/cypress/e2e/patient_spec/patient_crud.cy.ts index ff8ffaefcfc..b9e03851897 100644 --- a/cypress/e2e/patient_spec/patient_crud.cy.ts +++ b/cypress/e2e/patient_spec/patient_crud.cy.ts @@ -28,7 +28,7 @@ describe("Patient Creation with consultation", () => { cy.get("#add-patient-details").should("be.visible"); cy.get("#add-patient-details").click(); cy.get("input[name='facilities']") - .type("cypress facility") + .type("dummy facility") .then(() => { cy.get("[role='option']").first().click(); }); diff --git a/src/Components/Form/FormFields/CheckBoxFormField.tsx b/src/Components/Form/FormFields/CheckBoxFormField.tsx index 90c4513f4b7..f3382d522de 100644 --- a/src/Components/Form/FormFields/CheckBoxFormField.tsx +++ b/src/Components/Form/FormFields/CheckBoxFormField.tsx @@ -14,6 +14,7 @@ export default function CheckBoxFormField(props: FormFieldBaseProps) { name={field.name} checked={field.value} onChange={(e) => field.handleChange(e.target.checked)} + disabled={field.disabled} /> (); const [isLoading, setIsLoading] = useState(false); const [notes, setNotes] = useState(""); + const [isCustomTime, setIsCustomTime] = useState(false); + const [customTime, setCustomTime] = useState( + dayjs().format("YYYY-MM-DDTHH:mm") + ); return ( props.onClose(false)} - // variant="primary" onConfirm={async () => { setIsLoading(true); - const res = await dispatch(props.actions.administer({ notes })); + const res = await dispatch( + props.actions.administer({ + notes, + administered_date: isCustomTime ? customTime : undefined, + }) + ); if (res.status === 201) { Success({ msg: t("medicines_administered") }); } @@ -61,15 +72,43 @@ export default function AdministerMedicine({ prescription, ...props }: Props) { readonly actions={props.actions} /> - setNotes(value)} - errorClassName="hidden" - disabled={isLoading} - /> + +
+ setNotes(value)} + errorClassName="hidden" + disabled={isLoading} + /> +
+ { + setIsCustomTime(value); + if (!value) { + setCustomTime(dayjs().format("YYYY-MM-DDTHH:mm")); + } + }} + errorClassName="hidden" + /> + setCustomTime(value)} + disabled={!isCustomTime} + min={dayjs(prescription.created_date).format("YYYY-MM-DDTHH:mm")} + max={dayjs().format("YYYY-MM-DDTHH:mm")} + /> +
+
); diff --git a/src/Components/Medicine/MedicineAdministration.tsx b/src/Components/Medicine/MedicineAdministration.tsx index 16926b32f7b..5d8347ba5a5 100644 --- a/src/Components/Medicine/MedicineAdministration.tsx +++ b/src/Components/Medicine/MedicineAdministration.tsx @@ -10,6 +10,8 @@ import { useDispatch } from "react-redux"; import { Error, Success } from "../../Utils/Notifications"; import { formatDateTime } from "../../Utils/utils"; import { useTranslation } from "react-i18next"; +import dayjs from "../../Utils/dayjs"; +import TextFormField from "../Form/FormFields/TextFormField"; interface Props { prescriptions: Prescription[]; @@ -24,6 +26,8 @@ export default function MedicineAdministration(props: Props) { const [notes, setNotes] = useState( [] ); + const [isCustomTime, setIsCustomTime] = useState([]); + const [customTime, setCustomTime] = useState([]); const prescriptions = useMemo( () => @@ -36,13 +40,21 @@ export default function MedicineAdministration(props: Props) { useEffect(() => { setShouldAdminister(Array(prescriptions.length).fill(false)); setNotes(Array(prescriptions.length).fill("")); + setIsCustomTime(Array(prescriptions.length).fill(false)); + setCustomTime( + Array(prescriptions.length).fill(dayjs().format("YYYY-MM-DDTHH:mm")) + ); }, [props.prescriptions]); const handleSubmit = () => { const records: MedicineAdministrationRecord[] = []; prescriptions.forEach((prescription, i) => { if (shouldAdminister[i]) { - records.push({ prescription, notes: notes[i] }); + records.push({ + prescription, + notes: notes[i], + administered_date: isCustomTime[i] ? customTime[i] : undefined, + }); } }); @@ -73,7 +85,7 @@ export default function MedicineAdministration(props: Props) { actions={props.action(obj?.id ?? "")} selected={shouldAdminister[index]} > -
+
- - setNotes((notes) => { - const newNotes = [...notes]; - newNotes[index] = event.value; - return newNotes; - }) - } - errorClassName="hidden" - /> +
+ + setNotes((notes) => { + const newNotes = [...notes]; + newNotes[index] = event.value; + return newNotes; + }) + } + errorClassName="hidden" + /> +
+ { + setIsCustomTime((arr) => { + const newArr = [...arr]; + newArr[index] = value; + return newArr; + }); + if (!value) { + setCustomTime((arr) => { + const newArr = [...arr]; + newArr[index] = dayjs().format("YYYY-MM-DDTHH:mm"); + return newArr; + }); + } + }} + errorClassName="hidden" + /> + { + setCustomTime((arr) => { + const newArr = [...arr]; + newArr[index] = value; + return newArr; + }); + }} + disabled={!shouldAdminister[index] || !isCustomTime[index]} + min={dayjs(obj.created_date).format("YYYY-MM-DDTHH:mm")} + max={dayjs().format("YYYY-MM-DDTHH:mm")} + /> +
+
))} diff --git a/src/Components/Medicine/PrescriptionAdministrationsTable.tsx b/src/Components/Medicine/PrescriptionAdministrationsTable.tsx index c60d531baa3..81282126d7c 100644 --- a/src/Components/Medicine/PrescriptionAdministrationsTable.tsx +++ b/src/Components/Medicine/PrescriptionAdministrationsTable.tsx @@ -85,7 +85,7 @@ export default function PrescriptionAdministrationsTable({ {state?.prescriptions && ( diff --git a/src/Components/Medicine/models.ts b/src/Components/Medicine/models.ts index cb48e9cc174..62aea46b6d2 100644 --- a/src/Components/Medicine/models.ts +++ b/src/Components/Medicine/models.ts @@ -53,8 +53,8 @@ export type MedicineAdministrationRecord = { readonly id?: string; readonly prescription?: Prescription; notes: string; + administered_date?: string; readonly administered_by?: PerformedByModel; - readonly administered_date?: string; readonly created_date?: string; readonly modified_date?: string; }; From dcc7b70e1c77c11184e6606db058f30c95ca1ac9 Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 6 Sep 2023 13:35:44 +0530 Subject: [PATCH 09/50] Fix casing of plausible goal properties (#6229) --- src/Components/Facility/ConsultationDetails.tsx | 2 +- src/Components/Patient/PatientHome.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx index 257d3712544..6aa7b683ab5 100644 --- a/src/Components/Facility/ConsultationDetails.tsx +++ b/src/Components/Facility/ConsultationDetails.tsx @@ -226,7 +226,7 @@ export const ConsultationDetails = (props: any) => { triggerGoal("Patient Consultation Viewed", { facilityId: facilityId, consultationId: consultationId, - userID: authUser.id, + userId: authUser.id, }); }, []); diff --git a/src/Components/Patient/PatientHome.tsx b/src/Components/Patient/PatientHome.tsx index e8a666459a8..2565bce64ba 100644 --- a/src/Components/Patient/PatientHome.tsx +++ b/src/Components/Patient/PatientHome.tsx @@ -222,7 +222,7 @@ export const PatientHome = (props: any) => { fetchpatient(status); triggerGoal("Patient Profile Viewed", { facilityId: facilityId, - userID: authUser.id, + userId: authUser.id, }); }, [dispatch, fetchpatient] From 8a6e5266554524537b346f2407639fc09377ef5a Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 6 Sep 2023 13:57:18 +0000 Subject: [PATCH 10/50] Diagnosis: Adds field Principal Diagnosis (#6218) * adds field principle diagnosis * show in consultation details * fix typo * fix cypress * try adding wait * fix tests --- cypress/e2e/patient_spec/patient_crud.cy.ts | 11 ++- .../Facility/ConsultationDetails.tsx | 16 +++++ src/Components/Facility/ConsultationForm.tsx | 68 +++++++++++++++++++ src/Components/Facility/models.tsx | 1 + 4 files changed, 95 insertions(+), 1 deletion(-) diff --git a/cypress/e2e/patient_spec/patient_crud.cy.ts b/cypress/e2e/patient_spec/patient_crud.cy.ts index b9e03851897..46afbb13d03 100644 --- a/cypress/e2e/patient_spec/patient_crud.cy.ts +++ b/cypress/e2e/patient_spec/patient_crud.cy.ts @@ -186,15 +186,24 @@ describe("Patient Creation with consultation", () => { cy.get("#weight").click().type("70"); cy.get("#height").click().type("170"); cy.get("#patient_no").type("IP007"); + + cy.intercept("GET", "**/icd/**").as("getIcdResults"); cy.get( "#icd11_diagnoses_object input[placeholder='Select'][role='combobox']" ) .click() .type("1A"); - cy.wait(1000); cy.get("#icd11_diagnoses_object [role='option']") .contains("1A03 Intestinal infections due to Escherichia coli") .click(); + cy.get("label[for='icd11_diagnoses_object']").click(); + cy.wait("@getIcdResults").its("response.statusCode").should("eq", 200); + + cy.get("#icd11_principal_diagnosis [role='combobox']").click().type("1A"); + cy.get("#icd11_principal_diagnosis [role='option']") + .contains("1A03 Intestinal infections due to Escherichia coli") + .click(); + cy.get("#consultation_notes").click().type("generalnote"); cy.get("#verified_by").click().type("generalnote"); cy.get("#submit").click(); diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx index 6aa7b683ab5..30cbd1afdb7 100644 --- a/src/Components/Facility/ConsultationDetails.tsx +++ b/src/Components/Facility/ConsultationDetails.tsx @@ -430,6 +430,22 @@ export const ConsultationDetails = (props: any) => {
)*/} + {consultationData.icd11_principal_diagnosis && ( + + d.id === consultationData.icd11_principal_diagnosis + )!, + ]} + /> + )} + import("../Common/Loading")); const PageTitle = lazy(() => import("../Common/PageTitle")); @@ -80,6 +81,7 @@ type FormDetails = { referred_to_external?: string; icd11_diagnoses_object: ICD11DiagnosisModel[]; icd11_provisional_diagnoses_object: ICD11DiagnosisModel[]; + icd11_principal_diagnosis?: ICD11DiagnosisModel["id"]; verified_by: string; is_kasp: BooleanStrings; kasp_enabled_date: null; @@ -124,6 +126,7 @@ const initForm: FormDetails = { referred_to_external: "", icd11_diagnoses_object: [], icd11_provisional_diagnoses_object: [], + icd11_principal_diagnosis: undefined, verified_by: "", is_kasp: "false", kasp_enabled_date: null, @@ -551,6 +554,42 @@ export const ConsultationForm = (props: any) => { return; } + case "icd11_principal_diagnosis": { + if (!state.form[field]) { + errors[field] = "Please select Principal Diagnosis"; + invalidForm = true; + break; + } + + if ( + state.form[field] && + state.form["icd11_diagnoses_object"].length && + !state.form["icd11_diagnoses_object"] + .map((d) => d.id) + .includes(state.form[field]!) + ) { + errors[field] = + "Please select Principal Diagnosis from Final Diagnosis"; + invalidForm = true; + break; + } + + if ( + state.form[field] && + state.form["icd11_provisional_diagnoses_object"].length && + !state.form["icd11_provisional_diagnoses_object"] + .map((d) => d.id) + .includes(state.form[field]!) + ) { + errors[field] = + "Please select Principal Diagnosis from Provisional Diagnosis"; + invalidForm = true; + break; + } + + return; + } + default: return; } @@ -630,6 +669,7 @@ export const ConsultationForm = (props: any) => { state.form.icd11_provisional_diagnoses_object.map( (o: ICD11DiagnosisModel) => o.id ), + icd11_principal_diagnosis: state.form.icd11_principal_diagnosis, verified_by: state.form.verified_by, investigation: state.form.InvestigationAdvice, procedure: state.form.procedures, @@ -718,6 +758,18 @@ export const ConsultationForm = (props: any) => { verified_by: "Declared Dead", }, }); + } else if ( + event.name === "icd11_diagnoses_object" || + event.name === "icd11_provisional_diagnoses_object" + ) { + dispatch({ + type: "set_form", + form: { + ...state.form, + [event.name]: event.value, + icd11_principal_diagnosis: undefined, + }, + }); } else { dispatch({ type: "set_form", @@ -1152,6 +1204,22 @@ export const ConsultationForm = (props: any) => { label="Final Diagnosis" />
+ +
+ option.label} + optionValue={(option) => option.id} + required + /> +
diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index 5c24c80687a..9666f20cfb8 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -112,6 +112,7 @@ export interface ConsultationModel { diagnosis?: string; icd11_diagnoses_object?: ICD11DiagnosisModel[]; icd11_provisional_diagnoses_object?: ICD11DiagnosisModel[]; + icd11_principal_diagnosis?: ICD11DiagnosisModel["id"]; verified_by?: string; suggestion_text?: string; symptoms?: Array; From b94851c0e40d0810d30d4c677a5f0d060e9cbe9a Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Wed, 6 Sep 2023 19:27:48 +0530 Subject: [PATCH 11/50] open pdf file preview in new tab (#6226) --- src/Components/Patient/FileUpload.tsx | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Components/Patient/FileUpload.tsx b/src/Components/Patient/FileUpload.tsx index 3fb0fde6229..fe0881e6994 100644 --- a/src/Components/Patient/FileUpload.tsx +++ b/src/Components/Patient/FileUpload.tsx @@ -481,15 +481,20 @@ export const FileUpload = (props: FileUploadProps) => { }; const responseData = await dispatch(retrieveUpload(data, id)); const file_extension = getExtension(responseData.data.read_signed_url); - setFileState({ - ...file_state, - open: true, - name: responseData.data.name, - extension: file_extension, - isImage: ExtImage.includes(file_extension), - }); - downloadFileUrl(responseData.data.read_signed_url); - setFileUrl(responseData.data.read_signed_url); + if (file_extension === "pdf") { + window.open(responseData.data.read_signed_url, "_blank"); + setFileState({ ...file_state, open: false }); + } else { + setFileState({ + ...file_state, + open: true, + name: responseData.data.name, + extension: file_extension, + isImage: ExtImage.includes(file_extension), + }); + downloadFileUrl(responseData.data.read_signed_url); + setFileUrl(responseData.data.read_signed_url); + } }; const validateEditFileName = (name: any) => { @@ -1457,7 +1462,7 @@ export const FileUpload = (props: FileUploadProps) => {
)} -
+
Date: Wed, 6 Sep 2023 19:28:41 +0530 Subject: [PATCH 12/50] Changed weekly to average weekly for working hours (#6228) * Added padding to count block on patients page * fixed date format in asset manage page * Merged configure facility and configure health facility in one page * removed commented code * changed weekly to average weekly for working hours --- src/Components/Users/ManageUsers.tsx | 13 ++++++++----- src/Components/Users/UserProfile.tsx | 9 ++++----- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx index 99cb11e7e6f..d2e0ca415e3 100644 --- a/src/Components/Users/ManageUsers.tsx +++ b/src/Components/Users/ManageUsers.tsx @@ -387,7 +387,10 @@ export default function ManageUsers() { )}
- + {user.weekly_working_hours ? ( {user.weekly_working_hours} hours @@ -438,7 +441,7 @@ export default function ManageUsers() { }} > -

Set weekly working hours

+

Set Average weekly working hours

)} @@ -492,7 +495,7 @@ export default function ManageUsers() { open={expandWorkingHours} setOpen={setExpandWorkingHours} slideFrom="right" - title="Weekly working hours" + title="Average weekly working hours" dialogClass="md:w-[400px]" onCloseClick={() => { setWeeklyHours(0); @@ -500,7 +503,7 @@ export default function ManageUsers() { >
- Set weekly working hours for {selectedUser} + Set Average weekly working hours for {selectedUser}
168 - ? "Weekly working hours should be between 0 and 168" + ? "Average weekly working hours should be between 0 and 168" : "" } required diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index 37178a2e0cc..3fb4bccc7c7 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -264,7 +264,7 @@ export default function UserProfile() { !/^\d+$/.test(states.form[field] ?? "") ) { errors[field] = - "Weekly working hours must be a number between 0 and 168"; + "Average weekly working hours must be a number between 0 and 168"; invalidForm = true; } return; @@ -416,7 +416,7 @@ export default function UserProfile() { }; return (
-
+
@@ -559,7 +559,7 @@ export default function UserProfile() {
- Weekly working hours + Average weekly working hours
{details.weekly_working_hours ?? "-"} @@ -568,7 +568,6 @@ export default function UserProfile() {
)} - {showEdit && (
@@ -661,7 +660,7 @@ export default function UserProfile() { Date: Thu, 7 Sep 2023 15:18:28 +0530 Subject: [PATCH 13/50] Fix fullscreen button for IOS (#6225) * Fix fullscreen button for IOS * Fix fullscreen --- src/Common/hooks/useFullscreen.ts | 18 ++++++++++++++++-- src/Common/hooks/useMessageListener.ts | 6 +++--- .../Facility/ConsultationDetails.tsx | 8 ++++---- src/Components/Facility/Consultations/Feed.tsx | 9 ++++++++- src/Components/Facility/DischargeModal.tsx | 2 +- 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/Common/hooks/useFullscreen.ts b/src/Common/hooks/useFullscreen.ts index e409af1174c..f00dbfb0c5b 100644 --- a/src/Common/hooks/useFullscreen.ts +++ b/src/Common/hooks/useFullscreen.ts @@ -18,11 +18,25 @@ export default function useFullscreen(): [ document.removeEventListener("fullscreenchange", onFullscreenChange); }, []); + function openFullscreen(elem: HTMLElement) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (elem.webkitEnterFullscreen) elem.webkitEnterFullscreen(); // Safari + else elem.requestFullscreen(); + } + + function exitFullscreen(elem: HTMLElement) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (elem.webkitExitFullscreen) elem.webkitExitFullscreen(); // Safari + else document.exitFullscreen(); + } + const setFullscreen = (value: boolean, element?: HTMLElement) => { const fullscreenElement = element ?? document.documentElement; - if (value) fullscreenElement.requestFullscreen(); - else document.exitFullscreen(); + if (value) openFullscreen(fullscreenElement); + else exitFullscreen(fullscreenElement); }; return [isFullscreen, setFullscreen]; diff --git a/src/Common/hooks/useMessageListener.ts b/src/Common/hooks/useMessageListener.ts index eb5673da7fc..9dba3a18e58 100644 --- a/src/Common/hooks/useMessageListener.ts +++ b/src/Common/hooks/useMessageListener.ts @@ -5,12 +5,12 @@ type onMessage = (data: any) => void; export const useMessageListener = (onMessage: onMessage) => { useEffect(() => { const handleMessage = (e: MessageEvent) => { - onMessage(e.data); + onMessage?.(e.data); }; - navigator.serviceWorker.addEventListener("message", handleMessage); + navigator.serviceWorker?.addEventListener?.("message", handleMessage); return () => { - navigator.serviceWorker.removeEventListener("message", handleMessage); + navigator.serviceWorker?.removeEventListener?.("message", handleMessage); }; }); }; diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx index 30cbd1afdb7..194d6629fc8 100644 --- a/src/Components/Facility/ConsultationDetails.tsx +++ b/src/Components/Facility/ConsultationDetails.tsx @@ -120,10 +120,10 @@ export const ConsultationDetails = (props: any) => { ]); const { middleware_address } = facilityRes.data as FacilityModel; - const assetBeds = assetBedRes.data.results as AssetBedModel[]; + const assetBeds = assetBedRes?.data?.results as AssetBedModel[]; - const monitorBedData = assetBeds.find( - (i) => i.asset_object.asset_class === AssetClass.HL7MONITOR + const monitorBedData = assetBeds?.find( + (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR ); setMonitorBedData(monitorBedData); const assetDataForMonitor = monitorBedData?.asset_object; @@ -146,7 +146,7 @@ export const ConsultationDetails = (props: any) => { bed_object: consultationData?.current_bed?.bed_object, } as AssetBedModel; } else { - ventilatorBedData = assetBeds.find( + ventilatorBedData = assetBeds?.find( (i) => i.asset_object.asset_class === AssetClass.VENTILATOR ); } diff --git a/src/Components/Facility/Consultations/Feed.tsx b/src/Components/Facility/Consultations/Feed.tsx index caca55102ca..07fd6f31739 100644 --- a/src/Components/Facility/Consultations/Feed.tsx +++ b/src/Components/Facility/Consultations/Feed.tsx @@ -98,7 +98,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { async (status: statusType) => { setIsLoading(true); const res = await dispatch(getConsultation(consultationId)); - if (!status.aborted && res.data) { + if (!status.aborted && res?.data) { const consultation = res.data as ConsultationModel; const consultationBedId = consultation.current_bed?.bed_object?.id; if (consultationBedId) { @@ -302,6 +302,12 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => { }); }, fullScreen: () => { + if (isIOS) { + const element = document.querySelector("video"); + if (!element) return; + setFullscreen(true, element as HTMLElement); + return; + } if (!liveFeedPlayerRef.current) return; setFullscreen( !isFullscreen, @@ -500,6 +506,7 @@ export const Feed: React.FC = ({ consultationId, facilityId }) => {
{["fullScreen", "reset", "updatePreset", "zoomIn", "zoomOut"].map( (button, index) => { + if (isIOS && button === "reset") return null; const option = cameraPTZ.find( (option) => option.action === button ); diff --git a/src/Components/Facility/DischargeModal.tsx b/src/Components/Facility/DischargeModal.tsx index 5e13b038bab..8376a0ad299 100644 --- a/src/Components/Facility/DischargeModal.tsx +++ b/src/Components/Facility/DischargeModal.tsx @@ -87,7 +87,7 @@ const DischargeModal = ({ }) ); - if (res.data?.results?.length) { + if (res?.data?.results?.length > 0) { setLatestClaim(res.data.results[0]); if (isCreateClaimLoading) Notification.Success({ msg: "Fetched Claim Approval Results" }); From 3f1a0cbe09d697a9b8cb42e9f69408dabf9a032d Mon Sep 17 00:00:00 2001 From: Shivam Jha <86483059+ShivamJhaa@users.noreply.github.com> Date: Thu, 7 Sep 2023 17:43:45 +0530 Subject: [PATCH 14/50] Added test for importing and configuring an asset and migrated patient_crud.ts to POM approach (#5870) * Added test for importing and configuring an asset * Revert few changes * nits * Migrated to POM approach * Migrated patient_crud to POM approach * reverted some chnages * FIx * Merge conflicts * Fixed test * add responsiveness to virtual nursing assistant card (#6130) * allow use stock as well (#6115) * fixed responsive issue of 'Update Log' button in patient consultation page (#6113) * fix multiple bed bug (#6111) * Fixed typo in Date format in Asset management (#6105) * Added padding to count block on patients page * fixed date format in asset manage page * show only items with no min value (#6103) * check for id in response (#6100) * Added Responsiveness to File upload (#6096) * add responsiveness * refactor * remove overlap (#6094) * fixed failing test * Fix * Fixed comments * fix comments * Fixed all comments --------- Co-authored-by: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Co-authored-by: Gokulram A Co-authored-by: Kshitij Verma <101321276+kshitijv256@users.noreply.github.com> Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- cypress/e2e/assets_spec/assets_manage.cy.ts | 68 ++++- cypress/e2e/patient_spec/patient_crud.cy.ts | 287 ++++++------------ cypress/fixtures/sampleAsset.xlsx | Bin 0 -> 45398 bytes cypress/pageobject/Asset/AssetCreation.ts | 97 +++++- .../pageobject/Patient/PatientConsultation.ts | 108 +++++++ cypress/pageobject/Patient/PatientCreation.ts | 127 ++++++++ cypress/pageobject/Patient/PatientUpdate.ts | 97 ++++++ cypress/pageobject/constants.ts | 4 + src/Components/Assets/AssetImportModal.tsx | 3 +- src/Components/Assets/AssetManage.tsx | 1 + src/Components/Assets/AssetsList.tsx | 6 +- src/Components/Patient/PatientHome.tsx | 15 +- 12 files changed, 612 insertions(+), 201 deletions(-) create mode 100644 cypress/fixtures/sampleAsset.xlsx create mode 100644 cypress/pageobject/Patient/PatientConsultation.ts create mode 100644 cypress/pageobject/Patient/PatientCreation.ts create mode 100644 cypress/pageobject/Patient/PatientUpdate.ts create mode 100644 cypress/pageobject/constants.ts diff --git a/cypress/e2e/assets_spec/assets_manage.cy.ts b/cypress/e2e/assets_spec/assets_manage.cy.ts index d04a7733c0d..af3efb917dd 100644 --- a/cypress/e2e/assets_spec/assets_manage.cy.ts +++ b/cypress/e2e/assets_spec/assets_manage.cy.ts @@ -1,4 +1,5 @@ /// +import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress"; import { AssetPage } from "../../pageobject/Asset/AssetCreation"; import { v4 as uuidv4 } from "uuid"; import LoginPage from "../../pageobject/Login/LoginPage"; @@ -84,7 +85,9 @@ describe("Asset", () => { "Test note for asset creation!" ); + assetPage.interceptAssetCreation(); assetPage.clickCreateAsset(); + assetPage.verifyAssetCreation(); assetPage.verifySuccessNotification("Asset created successfully"); assetSearchPage.typeSearchKeyword("New Test Asset 2"); @@ -104,7 +107,8 @@ describe("Asset", () => { "Manufacturer's Name Edited", "Customer Support's Name Edited", "Vendor's Name Edited", - "Test note for asset creation edited!" + "Test note for asset creation edited!", + "25122021" ); assetPage.clickUpdateAsset(); @@ -112,11 +116,71 @@ describe("Asset", () => { assetPage.verifySuccessNotification("Asset updated successfully"); }); + it("Configure an asset", () => { + assetPage.openCreatedAsset(); + assetPage.spyAssetConfigureApi(); + assetPage.configureAsset( + "Host name", + "192.168.1.64", + "remote_user", + "2jCkrCRSeahzKEU", + "d5694af2-21e2-4a39-9bad-2fb98d9818bd" + ); + assetPage.clickConfigureAsset(); + assetPage.verifyAssetConfiguration(200); + }); + + it("Add an vital monitor asset and configure it", () => { + assetPage.createAsset(); + assetPage.selectFacility("Dummy Facility 1"); + assetPage.selectLocation("Camera Loc"); + assetPage.selectAssetType("Internal"); + assetPage.selectAssetClass("HL7 Vitals Monitor"); + + const qr_id_1 = uuidv4(); + + assetPage.enterAssetDetails( + "New Test Asset Vital", + "Test Description", + "Working", + qr_id_1, + "Manufacturer's Name", + "2025-12-25", + "Customer Support's Name", + phone_number, + "email@support.com", + "Vendor's Name", + serialNumber, + "25122021", + "Test note for asset creation!" + ); + assetPage.interceptAssetCreation(); + assetPage.clickCreateAsset(); + assetPage.verifyAssetCreation(); + + assetSearchPage.typeSearchKeyword("New Test Asset Vital"); + assetSearchPage.pressEnter(); + + assetPage.openCreatedAsset(); + assetPage.configureVitalAsset("Host name", "192.168.1.64"); + assetPage.clickConfigureVital(); + }); + it("Delete an Asset", () => { assetPage.openCreatedAsset(); + assetPage.interceptDeleteAssetApi(); assetPage.deleteAsset(); + assetPage.verifyDeleteStatus(); + }); + + it("Import new asset", () => { + assetPage.selectImportOption(); + assetPage.selectImportFacility("Dummy Facility 1"); + assetPage.importAssetFile(); + assetPage.selectImportLocation("Camera Locations"); + assetPage.clickImportAsset(); - assetPage.verifySuccessNotification("Asset deleted successfully"); + assetPage.verifySuccessNotification("Assets imported successfully"); }); afterEach(() => { diff --git a/cypress/e2e/patient_spec/patient_crud.cy.ts b/cypress/e2e/patient_spec/patient_crud.cy.ts index 46afbb13d03..af639783a80 100644 --- a/cypress/e2e/patient_spec/patient_crud.cy.ts +++ b/cypress/e2e/patient_spec/patient_crud.cy.ts @@ -1,12 +1,13 @@ import { afterEach, before, beforeEach, cy, describe, it } from "local-cypress"; - -const username = "devdistrictadmin"; -const password = "Coronasafe@123"; -const phone_number = "9" + Math.floor(100000000 + Math.random() * 900000000); -const emergency_phone_number = - "9" + Math.floor(100000000 + Math.random() * 900000000); +import LoginPage from "../../pageobject/Login/LoginPage"; +import { PatientPage } from "../../pageobject/Patient/PatientCreation"; +import { UpdatePatientPage } from "../../pageobject/Patient/PatientUpdate"; +import { PatientConsultationPage } from "../../pageobject/Patient/PatientConsultation"; +import { + emergency_phone_number, + phone_number, +} from "../../pageobject/constants"; const yearOfBirth = "2023"; -let patient_url = ""; const calculateAge = () => { const currentYear = new Date().getFullYear(); @@ -14,8 +15,13 @@ const calculateAge = () => { }; describe("Patient Creation with consultation", () => { + const loginPage = new LoginPage(); + const patientPage = new PatientPage(); + const updatePatientPage = new UpdatePatientPage(); + const patientConsultationPage = new PatientConsultationPage(); + before(() => { - cy.loginByApi(username, password); + loginPage.loginAsDisctrictAdmin(); cy.saveLocalStorage(); }); @@ -25,208 +31,105 @@ describe("Patient Creation with consultation", () => { }); it("Create a new patient with no consultation", () => { - cy.get("#add-patient-details").should("be.visible"); - cy.get("#add-patient-details").click(); - cy.get("input[name='facilities']") - .type("dummy facility") - .then(() => { - cy.get("[role='option']").first().click(); - }); - cy.get("button").should("contain", "Select"); - cy.get("button").get("#submit").click(); - cy.get("#phone_number-div").type(phone_number); - cy.get("#emergency_phone_number-div").type(emergency_phone_number); - cy.get("#date_of_birth").should("be.visible").click(); - cy.get("#date-input").click().type("01082023"); - cy.get("[data-testid=name] input").type("Test E2E User"); - cy.get("[data-testid=Gender] button") - .click() - .then(() => { - cy.get("[role='option']").contains("Male").click(); - }); - cy.get("[data-testid=current-address] textarea").type( - "Test Patient Address" + patientPage.createPatient(); + patientPage.selectFacility("dummy facility"); + patientPage.enterPatientDetails( + phone_number, + emergency_phone_number, + "Test E2E User", + "Male", + "Test Patient Address", + "682001", + "1: PAZHAMTHOTTAM", + "O+", + "01012001" ); - cy.get("[data-testid=permanent-address] input").check(); - cy.get("#pincode").type("682001"); - cy.get("[data-testid=localbody] button") - .click() - .then(() => { - cy.get("[role='option']").first().click(); - }); - cy.get("[data-testid=ward-respective-lsgi] button") - .click() - .then(() => { - cy.get("[role='option']").contains("1: PAZHAMTHOTTAM").click(); - }); - cy.get("[name=medical_history_check_1]").check(); - cy.get("[data-testid=blood-group] button") - .click() - .then(() => { - cy.get("[role='option']").contains("O+").click(); - }); - cy.get("button[data-testid='submit-button']").click(); + patientPage.clickCreatePatient(); - cy.get("h2").should("contain", "Create Consultation"); - cy.url().should("include", "/patient"); - cy.url().then((url) => { - cy.log(url); - patient_url = url.split("/").slice(0, -1).join("/"); - cy.log(patient_url); - }); + patientPage.verifyPatientIsCreated(); + patientPage.saveCreatedPatientUrl(); }); it("Patient Detail verification post registration", () => { - cy.log(patient_url); - cy.awaitUrl(patient_url); - cy.url().should("include", "/facility/"); - cy.get("[data-testid=patient-dashboard]").should("contain", calculateAge()); - cy.get("[data-testid=patient-dashboard]").should( - "contain", - "Test E2E User" - ); - cy.get("[data-testid=patient-dashboard]").should("contain", phone_number); - cy.get("[data-testid=patient-dashboard]").should( - "contain", - emergency_phone_number + patientPage.interceptFacilities(); + patientPage.visitCreatedPatient(); + patientPage.verifyStatusCode(); + const age = calculateAge(); + patientPage.verifyPatientDetails( + age, + "Test E2E User", + phone_number, + emergency_phone_number, + yearOfBirth, + "O+" ); - cy.get("[data-testid=patient-dashboard]").should("contain", yearOfBirth); - cy.get("[data-testid=patient-dashboard]").should("contain", "O+"); }); it("Edit the patient details", () => { - cy.intercept("GET", "**/facility/*/patient/**").as("getFacilities"); - cy.awaitUrl(patient_url + "/update"); - cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); - cy.wait(10000); - cy.get("#address").scrollIntoView(); - cy.get("#address").should("be.visible"); - cy.get("#address").type("Test Patient Address Edited"); - cy.get("[data-testid=name] input").clear(); - cy.get("[data-testid=name] input").type("Test E2E User Edited"); - cy.get("#phone_number-div").clear(); - cy.get("#phone_number-div").type("+91").type(phone_number); - cy.get("#emergency_phone_number-div").clear(); - cy.get("#emergency_phone_number-div") - .type("+91") - .type(emergency_phone_number); - cy.get("#present_health").type("Severe Cough"); - cy.get("#ongoing_medication").type("Paracetamol"); - cy.get("#allergies").type("Dust"); - cy.get("[name=medical_history_check_1]").uncheck(); - cy.get("[name=medical_history_check_2]").check(); - cy.get("#medical_history_2").type("2 months ago"); - cy.get("[name=medical_history_check_3]").check(); - cy.get("#medical_history_3").type("1 month ago"); - cy.get("button").get("[data-testid=add-insurance-button]").click(); - cy.get("#subscriber_id").type("SUB123"); - cy.get("#policy_id").type("P123"); - cy.get("#insurer_id").type("GICOFINDIA"); - cy.get("#insurer_name").type("GICOFINDIA"); - cy.get("[data-testid=blood-group] button") - .click() - .then(() => { - cy.get("[role='option']").contains("O+").click(); - }); - cy.get("button[data-testid='submit-button']").click(); - cy.url().should("include", "/patient"); - cy.url().then((url) => { - cy.log(url); - patient_url = url.split("/").slice(0, -1).join("/"); - cy.log(patient_url); - }); - }); - - it("Patient Detail verification post edit", () => { - cy.log(patient_url); - cy.awaitUrl(patient_url); - cy.url().should("include", "/facility/"); - cy.get("[data-testid=patient-dashboard]").should( - "contain", - "Test E2E User Edited" - ); - cy.get("[data-testid=patient-dashboard]").should("contain", phone_number); - const patientDetails_values: string[] = [ + patientPage.interceptFacilities(); + patientPage.visitUpdatePatientUrl(); + patientPage.verifyStatusCode(); + updatePatientPage.enterPatientDetails( + "Test E2E User Edited", + "O+", + phone_number, + emergency_phone_number, + "Test Patient Address Edited", "Severe Cough", "Paracetamol", "Dust", - "Diabetes", - "2 months ago", - "Heart Disease", - "1 month ago", - ]; + ["2 months ago", "1 month ago"], + "SUB123", + "P123", + "GICOFINDIA", + "GICOFINDIA" + ); + updatePatientPage.clickUpdatePatient(); - patientDetails_values.forEach((value) => { - cy.get("[data-testid=patient-details]").should("contain", value); - }); + updatePatientPage.verifyPatientUpdated(); + updatePatientPage.saveUpdatedPatientUrl(); }); - it("Create a New consultation to existing patient", () => { - cy.intercept("GET", "**/api/v1/patient/**").as("getFacilities"); - cy.visit(patient_url + "/consultation"); - cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); - cy.get("#history_of_present_illness").scrollIntoView; - cy.get("#history_of_present_illness").should("be.visible"); - cy.get("#history_of_present_illness").click().type("histroy"); - cy.get("#consultation_status") - .click() - .then(() => { - cy.get("[role='option']").contains("Out-patient (walk in)").click(); - }); - cy.get("#symptoms") - .click() - .then(() => { - cy.get("[role='option']").contains("ASYMPTOMATIC").click(); - }); - cy.get("#symptoms").click(); - - cy.get("#examination_details") - .click() - .type("Examination details and Clinical conditions"); - cy.get("#weight").click().type("70"); - cy.get("#height").click().type("170"); - cy.get("#patient_no").type("IP007"); - - cy.intercept("GET", "**/icd/**").as("getIcdResults"); - cy.get( - "#icd11_diagnoses_object input[placeholder='Select'][role='combobox']" - ) - .click() - .type("1A"); - cy.get("#icd11_diagnoses_object [role='option']") - .contains("1A03 Intestinal infections due to Escherichia coli") - .click(); - cy.get("label[for='icd11_diagnoses_object']").click(); - cy.wait("@getIcdResults").its("response.statusCode").should("eq", 200); + it("Patient Detail verification post edit", () => { + patientPage.interceptFacilities(); + updatePatientPage.visitUpdatedPatient(); + patientPage.verifyStatusCode(); - cy.get("#icd11_principal_diagnosis [role='combobox']").click().type("1A"); - cy.get("#icd11_principal_diagnosis [role='option']") - .contains("1A03 Intestinal infections due to Escherichia coli") - .click(); + updatePatientPage.verifyPatientDetails( + "Test E2E User Edited", + phone_number, + "Severe Cough", + "Paracetamol", + "Dust" + ); + }); - cy.get("#consultation_notes").click().type("generalnote"); - cy.get("#verified_by").click().type("generalnote"); - cy.get("#submit").click(); + it("Create a New consultation to existing patient", () => { + patientPage.interceptFacilities(); + updatePatientPage.visitConsultationPage(); + patientPage.verifyStatusCode(); + patientConsultationPage.fillIllnessHistory("history"); + patientConsultationPage.selectConsultationStatus("Out-patient (walk in)"); + patientConsultationPage.selectSymptoms("ASYMPTOMATIC"); + + patientConsultationPage.enterConsultationDetails( + "Examination details and Clinical conditions", + "70", + "170", + "IP007", + "generalnote", + "generalnote" + ); + patientConsultationPage.submitConsultation(); // Below code for the prescription module only present while creating a new consultation - cy.contains("button", "Add Prescription Medication") - .should("be.visible") - .click(); - cy.intercept("GET", "**/api/v1/medibase/**").as("getFacilities"); - cy.get( - "div#medicine_object input[placeholder='Select'][role='combobox']" - ).click(); - cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); - cy.get("div#medicine_object input[placeholder='Select'][role='combobox']") - .click() - .type("dolo{enter}"); - cy.get("#dosage").type("3", { force: true }); - cy.get("#frequency") - .click() - .then(() => { - cy.get("div#frequency [role='option']").contains("Twice daily").click(); - }); - cy.get("button#submit").should("be.visible").click(); - cy.get("[data-testid='return-to-patient-dashboard']").click(); + patientConsultationPage.clickAddPrescription(); + patientConsultationPage.interceptMediaBase(); + patientConsultationPage.selectMedicinebox(); + patientConsultationPage.waitForMediabaseStatusCode(); + patientConsultationPage.prescribeMedicine(); + patientConsultationPage.enterDosage("3"); + patientConsultationPage.selectDosageFrequency("Twice daily"); + patientConsultationPage.submitPrescriptionAndReturn(); }); afterEach(() => { diff --git a/cypress/fixtures/sampleAsset.xlsx b/cypress/fixtures/sampleAsset.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..f8e234ce4477934a0426a088783ba4988500f7aa GIT binary patch literal 45398 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

3{GwPx#=zj(sy?FWW-|fEUs= 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 literal 0 HcmV?d00001 diff --git a/cypress/pageobject/Asset/AssetCreation.ts b/cypress/pageobject/Asset/AssetCreation.ts index d24c93d0197..fdaf802d4b7 100644 --- a/cypress/pageobject/Asset/AssetCreation.ts +++ b/cypress/pageobject/Asset/AssetCreation.ts @@ -1,4 +1,5 @@ // assetPage.ts +import { cy, expect } from "local-cypress"; export class AssetPage { createAsset() { @@ -74,6 +75,14 @@ export class AssetPage { cy.get("[data-testid=asset-notes-input] textarea").type(notes); } + interceptAssetCreation() { + cy.intercept("POST", "**/api/v1/asset/").as("createAsset"); + } + + verifyAssetCreation() { + cy.wait("@createAsset").its("response.statusCode").should("eq", 201); + } + clickCreateAsset() { cy.get("#submit").contains("Create Asset").click(); } @@ -87,7 +96,9 @@ export class AssetPage { } openCreatedAsset() { + cy.intercept("GET", "**/api/v1/asset/**").as("getAsset"); cy.get("[data-testid=created-asset-list]").first().click(); + cy.wait("@getAsset").its("response.statusCode").should("eq", 200); } editAssetDetails( @@ -97,7 +108,8 @@ export class AssetPage { manufacturer: string, supportName: string, vendorName: string, - notes: string + notes: string, + lastServicedOn: string ) { cy.get("[data-testid=asset-update-button]").click(); cy.get("[data-testid=asset-name-input] input").clear().type(name); @@ -114,18 +126,71 @@ export class AssetPage { cy.get("[data-testid=asset-vendor-name-input] input") .clear() .type(vendorName); + cy.get( + "[data-testid=asset-last-serviced-on-input] input[type='text']" + ).click(); + cy.get("#date-input").click().type(lastServicedOn); cy.get("[data-testid=asset-notes-input] textarea").clear().type(notes); } + configureAsset( + hostName: string, + localIp: string, + userName: string, + password: string, + streamUuid: string + ) { + cy.get("[data-testid=asset-configure-button]").click(); + cy.get("[name=middleware_hostname]").type(hostName); + cy.get("[name=camera_address]").type(localIp); + cy.get("[name=username]").type(userName); + cy.get("[name=password]").type(password); + cy.get("[name=stream_uuid]").type(streamUuid); + } + + configureVitalAsset(hostName: string, localIp: string) { + cy.get("[data-testid=asset-configure-button]").click(); + cy.get("#middlewareHostname").type(hostName); + cy.get("#localipAddress").type(localIp); + } + + spyAssetConfigureApi() { + cy.intercept(/\/api\/v1\/asset/).as("asset"); + } + + verifyAssetConfiguration(statusCode: number) { + cy.wait("@asset").then((interception) => { + expect(interception.response.statusCode).to.equal(statusCode); + }); + } + + clickConfigureAsset() { + cy.get("#submit").contains("Set Configuration").click(); + } + + clickConfigureVital() { + cy.intercept("PATCH", "**/api/v1/asset/**").as("postConfiguration"); + cy.get("#submit").contains("Save Configuration").click(); + cy.wait("@postConfiguration").its("response.statusCode").should("eq", 200); + } + clickUpdateAsset() { cy.get("#submit").contains("Update").click(); } + interceptDeleteAssetApi() { + cy.intercept("DELETE", "**/api/v1/asset/**").as("deleteAsset"); + } + deleteAsset() { cy.get("[data-testid=asset-delete-button]").click(); cy.get("#submit").contains("Confirm").click(); } + verifyDeleteStatus() { + cy.wait("@deleteAsset").its("response.statusCode").should("eq", 204); + } + verifyEmptyAssetNameError() { cy.get("[data-testid=asset-name-input] span").should( "contain", @@ -160,4 +225,34 @@ export class AssetPage { "Please enter valid phone number" ); } + + selectImportFacility(facilityName: string) { + cy.get("input[name='facilities']") + .type(facilityName) + .then(() => { + cy.get("[role='option']").contains(facilityName).click(); + }); + } + + selectImportOption() { + cy.get("[data-testid=import-asset-button]").click(); + cy.get(".import-assets-button").click(); + } + + importAssetFile() { + cy.get("[data-testid=import-asset-file]") + .selectFile("cypress/fixtures/sampleAsset.xlsx", { force: true }) + .wait(100); + } + + selectImportLocation(locationName: string) { + cy.get("[data-testid=select-import-location]").click(); + cy.get("li[role=option]").contains(locationName).click(); + } + + clickImportAsset() { + cy.intercept("POST", "**/api/v1/asset/").as("importAsset"); + cy.get("#submit").contains("Import").click(); + cy.wait("@importAsset").its("response.statusCode").should("eq", 201); + } } diff --git a/cypress/pageobject/Patient/PatientConsultation.ts b/cypress/pageobject/Patient/PatientConsultation.ts new file mode 100644 index 00000000000..315ef455aaa --- /dev/null +++ b/cypress/pageobject/Patient/PatientConsultation.ts @@ -0,0 +1,108 @@ +export class PatientConsultationPage { + selectConsultationStatus(status: string) { + cy.get("#consultation_status") + .click() + .then(() => { + cy.get("[role='option']").contains(status).click(); + }); + } + + selectSymptoms(symptoms: string) { + cy.get("#symptoms") + .click() + .then(() => { + cy.get("[role='option']").contains(symptoms).click(); + }); + } + + fillIllnessHistory(history: string) { + cy.get("#history_of_present_illness").scrollIntoView; + cy.get("#history_of_present_illness").should("be.visible"); + cy.get("#history_of_present_illness").click().type(history); + } + + enterConsultationDetails( + examinationDetails: string, + weight: string, + height: string, + ipNumber: string, + consulationNotes: string, + verificationBy: string + ) { + cy.get("#symptoms").click(); + cy.get("#examination_details").click().type(examinationDetails); + cy.get("#weight").click().type(height); + cy.get("#height").click().type(weight); + cy.get("#patient_no").type(ipNumber); + cy.intercept("GET", "**/icd/**").as("getIcdResults"); + cy.get( + "#icd11_diagnoses_object input[placeholder='Select'][role='combobox']" + ) + .click() + .type("1A"); + cy.get("#icd11_diagnoses_object [role='option']") + .contains("1A03 Intestinal infections due to Escherichia coli") + .click(); + cy.get("label[for='icd11_diagnoses_object']").click(); + cy.wait("@getIcdResults").its("response.statusCode").should("eq", 200); + + cy.get("#icd11_principal_diagnosis [role='combobox']").click().type("1A"); + cy.get("#icd11_principal_diagnosis [role='option']") + .contains("1A03 Intestinal infections due to Escherichia coli") + .click(); + + cy.get("#consultation_notes").click().type(consulationNotes); + cy.get("#verified_by").click().type(verificationBy); + } + + submitConsultation() { + cy.get("#submit").click(); + } + + clickAddPrescription() { + cy.contains("button", "Add Prescription Medication") + .should("be.visible") + .click(); + } + + interceptMediaBase() { + cy.intercept("GET", "**/api/v1/medibase/**").as("getMediaBase"); + } + + prescribeMedicine() { + cy.get("div#medicine_object input[placeholder='Select'][role='combobox']") + .click() + .type("dolo{enter}"); + } + + selectMedicinebox() { + cy.get( + "div#medicine_object input[placeholder='Select'][role='combobox']" + ).click(); + } + + waitForMediabaseStatusCode() { + cy.wait("@getMediaBase").its("response.statusCode").should("eq", 200); + } + + enterDosage(doseAmount: string) { + cy.get("#dosage").type(doseAmount, { force: true }); + } + + selectDosageFrequency(frequency: string) { + cy.get("#frequency") + .click() + .then(() => { + cy.get("div#frequency [role='option']").contains(frequency).click(); + }); + } + + submitPrescriptionAndReturn() { + cy.intercept("POST", "**/api/v1/consultation/*/prescriptions/").as( + "submitPrescription" + ); + cy.get("button#submit").should("be.visible").click(); + cy.get("[data-testid='return-to-patient-dashboard']").click(); + cy.wait("@submitPrescription").its("response.statusCode").should("eq", 201); + } +} diff --git a/cypress/pageobject/Patient/PatientCreation.ts b/cypress/pageobject/Patient/PatientCreation.ts new file mode 100644 index 00000000000..9b8df4a287e --- /dev/null +++ b/cypress/pageobject/Patient/PatientCreation.ts @@ -0,0 +1,127 @@ +// PatientPage.ts + +let patient_url = ""; + +export class PatientPage { + createPatient() { + cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities"); + cy.get("#add-patient-details").should("be.visible"); + cy.get("#add-patient-details").click(); + cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); + } + + selectFacility(facilityName: string) { + cy.get("input[name='facilities']") + .type(facilityName) + .then(() => { + cy.get("[role='option']").first().click(); + }); + cy.get("button").should("contain", "Select"); + cy.get("button").get("#submit").click(); + } + + interceptCreatePatientAPI() { + cy.intercept("GET", "**/facility/*/patient/**").as("createPatient"); + } + + verifyCreatedPatientResponse() { + cy.wait("@createPatient").its("response.statusCode").should("eq", 200); + } + + enterPatientDetails( + phoneNumber: string, + emergencyPhoneNumber: string, + patientName: string, + gender: string, + address: string, + pincode: string, + wardName: string, + bloodGroup: string, + dateOfBirth: string + ) { + cy.get("#phone_number-div").type(phoneNumber); + cy.get("#emergency_phone_number-div").type(emergencyPhoneNumber); + cy.get("#date_of_birth").should("be.visible").click(); + cy.get("#date-input").click().type(dateOfBirth); + cy.get("[data-testid=name] input").type(patientName); + cy.get("[data-testid=Gender] button") + .click() + .then(() => { + cy.get("[role='option']").contains(gender).click(); + }); + cy.get("[data-testid=current-address] textarea").type(address); + cy.get("[data-testid=permanent-address] input").check(); + cy.get("#pincode").type(pincode); + cy.get("[data-testid=localbody] button") + .click() + .then(() => { + cy.get("[role='option']").first().click(); + }); + cy.get("[data-testid=ward-respective-lsgi] button") + .click() + .then(() => { + cy.get("[role='option']").contains(wardName).click(); + }); + cy.get("[name=medical_history_check_1]").check(); + cy.get("[data-testid=blood-group] button") + .click() + .then(() => { + cy.get("[role='option']").contains(bloodGroup).click(); + }); + } + + clickCreatePatient() { + cy.intercept("POST", "**/api/v1/patient/").as("createPatient"); + cy.get("button[data-testid='submit-button']").click(); + cy.wait("@createPatient").its("response.statusCode").should("eq", 201); + } + + verifyPatientIsCreated() { + cy.get("h2").should("contain", "Create Consultation"); + cy.url().should("include", "/patient"); + } + + saveCreatedPatientUrl() { + cy.url().then((url) => { + cy.log(url); + patient_url = url.split("/").slice(0, -1).join("/"); + cy.log(patient_url); + }); + } + + visitCreatedPatient() { + cy.awaitUrl(patient_url); + } + + verifyPatientDetails( + age: number, + patientName: string, + phoneNumber: string, + emergencyPhoneNumber: string, + yearOfBirth: string, + bloodGroup: string + ) { + cy.url().should("include", "/facility/"); + cy.get("[data-testid=patient-dashboard]").should("contain", age); + cy.get("[data-testid=patient-dashboard]").should("contain", patientName); + cy.get("[data-testid=patient-dashboard]").should("contain", phoneNumber); + cy.get("[data-testid=patient-dashboard]").should( + "contain", + emergencyPhoneNumber + ); + cy.get("[data-testid=patient-dashboard]").should("contain", yearOfBirth); + cy.get("[data-testid=patient-dashboard]").should("contain", bloodGroup); + } + + visitUpdatePatientUrl() { + cy.awaitUrl(patient_url + "/update"); + } + + interceptFacilities() { + cy.intercept("GET", "**/facility/*/patient/**").as("getFacilities"); + } + + verifyStatusCode() { + cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); + } +} diff --git a/cypress/pageobject/Patient/PatientUpdate.ts b/cypress/pageobject/Patient/PatientUpdate.ts new file mode 100644 index 00000000000..b26ef678679 --- /dev/null +++ b/cypress/pageobject/Patient/PatientUpdate.ts @@ -0,0 +1,97 @@ +let patient_url = ""; + +export class UpdatePatientPage { + enterPatientDetails( + patientName: string, + bloodGroup: string, + phoneNumber: string, + emergencyPhoneNumber: string, + address: string, + currentHealthCondition: string, + ongoingMedication: string, + allergies: string, + medicalHistory: string[], + subscriberId: string, + policyId: string, + insuranceId: string, + insuranceName: string + ) { + cy.wait(10000); + cy.get("#address").scrollIntoView(); + cy.get("#address").should("be.visible"); + cy.get("#address").type(address); + cy.get("[data-testid=name] input").clear(); + cy.get("[data-testid=name] input").type(patientName); + cy.get("#phone_number-div").clear(); + cy.get("#phone_number-div").type("+91").type(phoneNumber); + cy.get("#emergency_phone_number-div").clear(); + cy.get("#emergency_phone_number-div") + .type("+91") + .type(emergencyPhoneNumber); + cy.get("#present_health").type(currentHealthCondition); + cy.get("#ongoing_medication").type(ongoingMedication); + cy.get("#allergies").type(allergies); + cy.get("[name=medical_history_check_1]").uncheck(); + cy.get("[name=medical_history_check_2]").check(); + cy.get("#medical_history_2").type(medicalHistory[0]); + cy.get("[name=medical_history_check_3]").check(); + cy.get("#medical_history_3").type(medicalHistory[1]); + cy.get("button").get("[data-testid=add-insurance-button]").click(); + cy.get("#subscriber_id").type(subscriberId); + cy.get("#policy_id").type(policyId); + cy.get("#insurer_id").type(insuranceId); + cy.get("#insurer_name").type(insuranceName); + cy.get("[data-testid=blood-group] button") + .click() + .then(() => { + cy.get("[role='option']").contains(bloodGroup).click(); + }); + } + + clickUpdatePatient() { + cy.intercept("PUT", "**/api/v1/patient/**").as("updatePatient"); + cy.get("button").get("[data-testid=submit-button]").click(); + cy.wait("@updatePatient").its("response.statusCode").should("eq", 200); + } + + verifyPatientUpdated() { + cy.url().should("include", "/patient"); + } + + saveUpdatedPatientUrl() { + cy.url().then((url) => { + cy.log(url); + patient_url = url.split("/").slice(0, -1).join("/"); + cy.log(patient_url); + }); + } + + visitUpdatedPatient() { + cy.awaitUrl(patient_url); + } + + verifyPatientDetails( + patientName: string, + phoneNumber: string, + presentHealth: string, + ongoingMedication: string, + allergies: string + ) { + cy.url().should("include", "/facility/"); + cy.get("[data-testid=patient-dashboard]").should("contain", patientName); + cy.get("[data-testid=patient-dashboard]").should("contain", phoneNumber); + cy.get("[data-testid=patient-present-health]").should( + "contain", + presentHealth + ); + cy.get("[data-testid=patient-ongoing-medication]").should( + "contain", + ongoingMedication + ); + cy.get("[data-testid=patient-allergies]").should("contain", allergies); + } + + visitConsultationPage() { + cy.visit(patient_url + "/consultation"); + } +} diff --git a/cypress/pageobject/constants.ts b/cypress/pageobject/constants.ts new file mode 100644 index 00000000000..72e0d31c662 --- /dev/null +++ b/cypress/pageobject/constants.ts @@ -0,0 +1,4 @@ +export const phone_number = + "9" + Math.floor(100000000 + Math.random() * 900000000); +export const emergency_phone_number = + "9" + Math.floor(100000000 + Math.random() * 900000000); diff --git a/src/Components/Assets/AssetImportModal.tsx b/src/Components/Assets/AssetImportModal.tsx index b5b0110096a..548df2901ec 100644 --- a/src/Components/Assets/AssetImportModal.tsx +++ b/src/Components/Assets/AssetImportModal.tsx @@ -220,7 +220,7 @@ const AssetImportModal = ({ open, onClose, facility }: Props) => { -

+
{
- {config.enable_abdm ? ( - - ) : ( - <> - )} + + ); }; diff --git a/src/Redux/actions.tsx b/src/Redux/actions.tsx index e3212685d2f..6e0d91fc59d 100644 --- a/src/Redux/actions.tsx +++ b/src/Redux/actions.tsx @@ -979,6 +979,15 @@ export const healthFacilityActions = { facility_id: id, }); }, + + registerService: (id: string) => { + return fireRequest( + "registerHealthFacilityAsService", + [], + {}, + { facility_id: id } + ); + }, }; export const listAssetAvailability = (params: object) => diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 3efff35689e..393b9372ca5 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -931,6 +931,11 @@ const routes: Routes = { method: "PATCH", }, + registerHealthFacilityAsService: { + path: "/api/v1/abdm/health_facility/{facility_id}/register_service/", + method: "POST", + }, + // Asset Availability endpoints listAssetAvailability: { From 8bcf91dce41cb8c20d7b2b674550dcb73a0ca746 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Fri, 8 Sep 2023 06:22:14 +0000 Subject: [PATCH 18/50] cypress: fix inventory management (#6245) * fix cypress :/ * Update cypress/pageobject/Facility/FacilityCreation.ts * fix double click --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- cypress/pageobject/Facility/FacilityCreation.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cypress/pageobject/Facility/FacilityCreation.ts b/cypress/pageobject/Facility/FacilityCreation.ts index 3eac29f14c5..aa46d4f1105 100644 --- a/cypress/pageobject/Facility/FacilityCreation.ts +++ b/cypress/pageobject/Facility/FacilityCreation.ts @@ -119,6 +119,7 @@ class FacilityPage { } clickInventoryManagementOption() { + cy.get("#inventory-management", { timeout: 10000 }).should("be.visible"); cy.get("#inventory-management").click(); } @@ -162,8 +163,6 @@ class FacilityPage { cy.intercept("GET", "**/api/v1/facility/**").as("getFacilities"); cy.get("[id='facility-details']").first().click(); cy.wait("@getFacilities").its("response.statusCode").should("eq", 200); - cy.get("#manage-facility-dropdown button").should("be.visible"); - cy.get("[id=manage-facility-dropdown]").scrollIntoView().click(); } clickManageInventory() { From 9b45c5e9a496c54111e07232f1015c68ebb6205b Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Fri, 8 Sep 2023 19:24:23 +0530 Subject: [PATCH 19/50] Fix verified_by and Principal Diagnosis validation (#6247) * Fix verified_by and Principal Diagnosis validation * Apply suggestions from code review Co-authored-by: Rithvik Nishad --- src/Components/Facility/ConsultationForm.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index fbea6dd8a5a..97f35f292f8 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -538,7 +538,7 @@ export const ConsultationForm = (props: any) => { } case "verified_by": { - if (!state.form[field].replace(/\s/g, "").length) { + if (!state.form[field]) { errors[field] = "Please fill verified by"; invalidForm = true; break; @@ -570,6 +570,7 @@ export const ConsultationForm = (props: any) => { if ( state.form[field] && state.form["icd11_diagnoses_object"].length && + !state.form["icd11_provisional_diagnoses_object"] && !state.form["icd11_diagnoses_object"] .map((d) => d.id) .includes(state.form[field]!) @@ -583,6 +584,7 @@ export const ConsultationForm = (props: any) => { if ( state.form[field] && state.form["icd11_provisional_diagnoses_object"].length && + !state.form["icd11_diagnoses_object"] && !state.form["icd11_provisional_diagnoses_object"] .map((d) => d.id) .includes(state.form[field]!) From fb4e7bed464b858e5a127b9701c5caa1660a8447 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Fri, 8 Sep 2023 15:28:47 +0000 Subject: [PATCH 20/50] Consultation Form: skip validation for `verified_by` if `suggestion` is Declare Death and fix discharge not working on create consultation (#6248) * fix verified by validation * fix decalre dead --- src/Components/Facility/ConsultationForm.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index 97f35f292f8..49028a62953 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -538,7 +538,7 @@ export const ConsultationForm = (props: any) => { } case "verified_by": { - if (!state.form[field]) { + if (state.form.suggestion !== "DD" && !state.form[field]) { errors[field] = "Please fill verified by"; invalidForm = true; break; @@ -618,6 +618,7 @@ export const ConsultationForm = (props: any) => { }; const declareThePatientDead = async ( + id: string, cause_of_death: string, death_datetime: string, death_confirmed_doctor: string @@ -629,6 +630,7 @@ export const ConsultationForm = (props: any) => { discharge_notes: cause_of_death, death_datetime: death_datetime, death_confirmed_doctor: death_confirmed_doctor, + discharge_date: dayjs().toISOString(), }, { id } ) @@ -714,6 +716,7 @@ export const ConsultationForm = (props: any) => { if (data.suggestion === "DD") { await declareThePatientDead( + res.data.id, state.form.cause_of_death, state.form.death_datetime, state.form.death_confirmed_doctor From ca51ef15bad191bb89c59c1b8641320e817606f4 Mon Sep 17 00:00:00 2001 From: "Tasnimul H. Tauhid" Date: Mon, 11 Sep 2023 13:57:31 +0530 Subject: [PATCH 21/50] Refactor Consultation Details (#6258) --- .../Facility/ConsultationDetails.tsx | 1324 ----------------- .../ConsultationABGTab.tsx | 22 + .../ConsultationDialysisTab.tsx | 14 + .../ConsultationFeedTab.tsx | 22 + .../ConsultationFilesTab.tsx | 18 + .../ConsultationInvestigationsTab.tsx | 39 + .../ConsultationMedicinesTab.tsx | 19 + .../ConsultationNeurologicalMonitoringTab.tsx | 24 + .../ConsultationNeutritionTab.tsx | 18 + .../ConsultationNursingTab.tsx | 18 + .../ConsultationPressureSoreTab.tsx | 14 + .../ConsultationSummaryTab.tsx | 22 + .../ConsultationUpdatesTab.tsx | 690 +++++++++ .../ConsultationVentilatorTab.tsx | 22 + .../Facility/ConsultationDetails/index.tsx | 501 +++++++ 15 files changed, 1443 insertions(+), 1324 deletions(-) delete mode 100644 src/Components/Facility/ConsultationDetails.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx create mode 100644 src/Components/Facility/ConsultationDetails/index.tsx diff --git a/src/Components/Facility/ConsultationDetails.tsx b/src/Components/Facility/ConsultationDetails.tsx deleted file mode 100644 index 401f113a812..00000000000 --- a/src/Components/Facility/ConsultationDetails.tsx +++ /dev/null @@ -1,1324 +0,0 @@ -import { AssetBedModel, AssetClass, AssetData } from "../Assets/AssetTypes"; -import { - CONSULTATION_TABS, - DISCHARGE_REASONS, - GENDER_TYPES, - OptionsType, - SYMPTOM_CHOICES, -} from "../../Common/constants"; -import { - BedModel, - ConsultationModel, - FacilityModel, - ICD11DiagnosisModel, -} from "./models"; -import { - getConsultation, - getPatient, - getPermittedFacility, - listAssetBeds, -} from "../../Redux/actions"; -import { statusType, useAbortableEffect } from "../../Common/utils"; -import { lazy, useCallback, useEffect, useState } from "react"; - -import { ABGPlots } from "./Consultations/ABGPlots"; -import ButtonV2 from "../Common/components/ButtonV2"; -import CareIcon from "../../CAREUI/icons/CareIcon"; -import Chip from "../../CAREUI/display/Chip"; -import { DailyRoundsList } from "./Consultations/DailyRoundsList"; -import { DialysisPlots } from "./Consultations/DialysisPlots"; -import DischargeModal from "./DischargeModal"; -import DischargeSummaryModal from "./DischargeSummaryModal"; -import DoctorVideoSlideover from "./DoctorVideoSlideover"; -import { Feed } from "./Consultations/Feed"; -import { FileUpload } from "../Patient/FileUpload"; -import HL7PatientVitalsMonitor from "../VitalsMonitor/HL7PatientVitalsMonitor"; -import InvestigationTab from "./Investigations/investigationsTab"; -import { make as Link } from "../Common/components/Link.bs"; -import { NeurologicalTable } from "./Consultations/NeurologicalTables"; -import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; -import { NursingPlot } from "./Consultations/NursingPlot"; -import { NutritionPlots } from "./Consultations/NutritionPlots"; -import PatientInfoCard from "../Patient/PatientInfoCard"; -import { PatientModel } from "../Patient/models"; -import PrescriptionsTable from "../Medicine/PrescriptionsTable"; -import { PressureSoreDiagrams } from "./Consultations/PressureSoreDiagrams"; -import { PrimaryParametersPlot } from "./Consultations/PrimaryParametersPlot"; -import ReadMore from "../Common/components/Readmore"; -import VentilatorPatientVitalsMonitor from "../VitalsMonitor/VentilatorPatientVitalsMonitor"; -import { VentilatorPlot } from "./Consultations/VentilatorPlot"; -import { formatDate, formatDateTime, relativeTime } from "../../Utils/utils"; - -import { navigate } from "raviger"; -import { useDispatch } from "react-redux"; -import { useQueryParams } from "raviger"; -import { useTranslation } from "react-i18next"; -import { triggerGoal } from "../Common/Plausible"; -import useVitalsAspectRatioConfig from "../VitalsMonitor/useVitalsAspectRatioConfig"; -import useAuthUser from "../../Common/hooks/useAuthUser"; -import PrescriptionAdministrationsTable from "../Medicine/PrescriptionAdministrationsTable"; - -const Loading = lazy(() => import("../Common/Loading")); -const PageTitle = lazy(() => import("../Common/PageTitle")); -const symptomChoices = [...SYMPTOM_CHOICES]; - -export const ConsultationDetails = (props: any) => { - const { t } = useTranslation(); - const { facilityId, patientId, consultationId } = props; - const tab = props.tab.toUpperCase(); - const dispatch: any = useDispatch(); - const [isLoading, setIsLoading] = useState(false); - const [showDoctors, setShowDoctors] = useState(false); - const [qParams, _] = useQueryParams(); - - const [consultationData, setConsultationData] = useState( - {} as ConsultationModel - ); - const [patientData, setPatientData] = useState({}); - const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] = - useState(false); - const [openDischargeDialog, setOpenDischargeDialog] = useState(false); - const [showAutomatedRounds, setShowAutomatedRounds] = useState(true); - - const getPatientGender = (patientData: any) => - GENDER_TYPES.find((i) => i.id === patientData.gender)?.text; - - const getPatientAddress = (patientData: any) => - `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`; - - const getPatientComorbidities = (patientData: any) => { - if (patientData?.medical_history?.length) { - const medHis = patientData.medical_history; - return medHis.map((item: any) => item.disease).join(", "); - } else { - return "None"; - } - }; - - const [hl7SocketUrl, setHL7SocketUrl] = useState(); - const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState(); - const [monitorBedData, setMonitorBedData] = useState(); - const [ventilatorBedData, setVentilatorBedData] = useState(); - const authUser = useAuthUser(); - - useEffect(() => { - if ( - !consultationData.facility || - !consultationData.current_bed?.bed_object.id - ) - return; - - const fetchData = async () => { - const [facilityRes, assetBedRes] = await Promise.all([ - dispatch(getPermittedFacility(consultationData.facility as any)), - dispatch( - listAssetBeds({ - facility: consultationData.facility as any, - bed: consultationData.current_bed?.bed_object.id, - }) - ), - ]); - - const { middleware_address } = facilityRes.data as FacilityModel; - const assetBeds = assetBedRes?.data?.results as AssetBedModel[]; - - const monitorBedData = assetBeds?.find( - (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR - ); - setMonitorBedData(monitorBedData); - const assetDataForMonitor = monitorBedData?.asset_object; - const hl7Meta = assetDataForMonitor?.meta; - const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address; - if (hl7Middleware && hl7Meta?.local_ip_address) { - setHL7SocketUrl( - `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}` - ); - } - - const consultationBedVentilator = - consultationData?.current_bed?.assets_objects?.find( - (i) => i.asset_class === AssetClass.VENTILATOR - ); - let ventilatorBedData; - if (consultationBedVentilator) { - ventilatorBedData = { - asset_object: consultationBedVentilator, - bed_object: consultationData?.current_bed?.bed_object, - } as AssetBedModel; - } else { - ventilatorBedData = assetBeds?.find( - (i) => i.asset_object.asset_class === AssetClass.VENTILATOR - ); - } - setVentilatorBedData(ventilatorBedData); - const ventilatorMeta = ventilatorBedData?.asset_object?.meta; - const ventilatorMiddleware = - ventilatorMeta?.middleware_hostname || middleware_address; - if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) { - setVentilatorSocketUrl( - `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}` - ); - } - - if ( - !(hl7Middleware && hl7Meta?.local_ip_address) && - !(ventilatorMiddleware && ventilatorMeta?.local_ip_address) - ) { - setHL7SocketUrl(undefined); - setVentilatorSocketUrl(undefined); - } - }; - - fetchData(); - }, [consultationData]); - - const fetchData = useCallback( - async (status: statusType) => { - setIsLoading(true); - const res = await dispatch(getConsultation(consultationId)); - if (!status.aborted) { - if (res?.data) { - const data: ConsultationModel = { - ...res.data, - symptoms_text: "", - }; - if (res.data.symptoms?.length) { - const symptoms = res.data.symptoms - .filter((symptom: number) => symptom !== 9) - .map((symptom: number) => { - const option = symptomChoices.find((i) => i.id === symptom); - return option ? option.text.toLowerCase() : symptom; - }); - data.symptoms_text = symptoms.join(", "); - } - setConsultationData(data); - const id = res.data.patient; - const patientRes = await dispatch(getPatient({ id })); - if (patientRes?.data) { - const patientGender = getPatientGender(patientRes.data); - const patientAddress = getPatientAddress(patientRes.data); - const patientComorbidities = getPatientComorbidities( - patientRes.data - ); - const data = { - ...patientRes.data, - gender: patientGender, - address: patientAddress, - comorbidities: patientComorbidities, - is_declared_positive: patientRes.data.is_declared_positive - ? "Yes" - : "No", - is_vaccinated: patientData.is_vaccinated ? "Yes" : "No", - }; - setPatientData(data); - } - } else { - navigate("/not-found"); - } - setIsLoading(false); - } - }, - [consultationId, dispatch, patientData.is_vaccinated] - ); - - useAbortableEffect((status: statusType) => { - fetchData(status); - triggerGoal("Patient Consultation Viewed", { - facilityId: facilityId, - consultationId: consultationId, - userId: authUser.id, - }); - }, []); - - const vitals = useVitalsAspectRatioConfig({ - default: undefined, - md: 8 / 11, - lg: 15 / 11, - xl: 13 / 11, - "2xl": 19 / 11, - "3xl": 23 / 11, - }); - - if (isLoading) { - return ; - } - - const tabButtonClasses = (selected: boolean) => - `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${ - selected === true ? "border-primary-500 text-primary-600 border-b-2" : "" - }`; - - const ShowDiagnosis = ({ - diagnoses = [], - label = "Diagnosis", - nshow = 2, - }: { - diagnoses: ICD11DiagnosisModel[] | undefined; - label: string; - nshow?: number; - }) => { - const [showMore, setShowMore] = useState(false); - - return diagnoses.length ? ( -
-

{label}

- - {diagnoses.slice(0, !showMore ? nshow : undefined).map((diagnosis) => ( -

{diagnosis.label}

- ))} - {diagnoses.length > nshow && ( - <> - {!showMore ? ( - setShowMore(true)} - className="cursor-pointer text-sm text-blue-600 hover:text-blue-300" - > - show more - - ) : ( - setShowMore(false)} - className="cursor-pointer text-sm text-blue-600 hover:text-blue-300" - > - show less - - )} - - )} -
- ) : null; - }; - - return ( -
- setOpenDischargeSummaryDialog(false)} - /> - - setOpenDischargeDialog(false)} - consultationData={consultationData} - /> - -
- -
-
- - -
- {consultationData.admitted_to && ( -
-
- Patient - {consultationData.discharge_date - ? " Discharged from" - : " Admitted to"} - - {consultationData.admitted_to} - -
- {(consultationData.admission_date ?? - consultationData.discharge_date) && ( -
- {relativeTime( - consultationData.discharge_date - ? consultationData.discharge_date - : consultationData.admission_date - )} -
- )} -
- {consultationData.admission_date && - formatDateTime(consultationData.admission_date)} - {consultationData.discharge_date && - ` - ${formatDateTime(consultationData.discharge_date)}`} -
-
- )} -
- -
-
- {/*consultationData.other_symptoms && ( -
- - Other Symptoms:{" "} - - {consultationData.other_symptoms} -
- )*/} - - {consultationData.icd11_principal_diagnosis && ( - - d.id === consultationData.icd11_principal_diagnosis - )!, - ]} - /> - )} - - - - - - {(consultationData.verified_by_object || - consultationData.deprecated_verified_by) && ( -
- - Verified By:{" "} - - {consultationData.verified_by_object - ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}` - : consultationData.deprecated_verified_by} - -
- )} -
-
- setOpenDischargeSummaryDialog(true)}> - - {t("discharge_summary")} - - - setOpenDischargeDialog(true)} - disabled={!!consultationData.discharge_date} - > - - {t("discharge_from_care")} - -
-
-
-
-
- Created: - {consultationData.created_date - ? formatDateTime(consultationData.created_date) - : "--:--"}{" "} - | -
- {consultationData.created_by && ( -
- {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `} - {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`} -
- )} -
-
-
- Last Modified: - {consultationData.modified_date - ? formatDateTime(consultationData.modified_date) - : "--:--"}{" "} - | -
- {consultationData.last_edited_by && ( -
- {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `} - {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`} -
- )} -
-
-
-
- -
-
-
- -
-
-
- {tab === "UPDATES" && ( -
- {!consultationData.discharge_date && - hl7SocketUrl && - ventilatorSocketUrl && ( -
-
-
- -
-
- -
-
-
- )} -
-
- -
- {!consultationData.discharge_date && - ((hl7SocketUrl && !ventilatorSocketUrl) || - (!hl7SocketUrl && ventilatorSocketUrl)) && ( -
- {(hl7SocketUrl || ventilatorSocketUrl) && ( -
- {hl7SocketUrl && ( -
- -
- )} - {ventilatorSocketUrl && ( -
- -
- )} -
- )} -
- )} - {consultationData.discharge_date && ( -
-
-

- Discharge Information -

-
-
- Reason {" - "} - - {DISCHARGE_REASONS.find( - (d) => - d.id === consultationData.discharge_reason - )?.text ?? "--"} - -
- {consultationData.discharge_reason === "REF" && ( -
- Referred Facility {" - "} - - {consultationData.referred_to_external || - consultationData.referred_to_object?.name || - "--"} - -
- )} - {consultationData.discharge_reason === "REC" && ( -
-
- Discharge Date {" - "} - - {consultationData.discharge_date - ? formatDate( - consultationData.discharge_date - ) - : "--/--/----"} - -
-
- Advice {" - "} - - {consultationData.discharge_notes ?? "--"} - -
-
- -
-
-
- -
-
- )} - {consultationData.discharge_reason === "EXP" && ( -
-
- Date of Death {" - "} - - {consultationData.death_datetime - ? formatDateTime( - consultationData.death_datetime - ) - : "--:--"} - -
-
- Cause of death {" - "} - - {consultationData.discharge_notes ?? "--"} - -
-
- Confirmed By {" - "} - - {consultationData.death_confirmed_doctor ?? - "--"} - -
-
- )} - {["REF", "LAMA"].includes( - consultationData.discharge_reason ?? "" - ) && ( -
-
- Discharge Date {" - "} - - {consultationData.discharge_date - ? formatDate( - consultationData.discharge_date - ) - : "--/--/----"} - -
-
- Notes {" - "} - - {consultationData.discharge_notes ?? "--"} - -
-
- )} -
-
-
- )} - {consultationData.symptoms_text && ( -
-
-

- Symptoms -

-
-
- Last Daily Update -
- {consultationData.last_daily_round - ?.additional_symptoms && ( - <> -
- {consultationData.last_daily_round?.additional_symptoms.map( - (symptom: any, index: number) => ( - choice.id === symptom - )?.text ?? "Err. Unknown" - } - size="small" - /> - ) - )} -
- {consultationData.last_daily_round - ?.other_symptoms && ( -
-
- Other Symptoms: -
- { - consultationData.last_daily_round - ?.other_symptoms - } -
- )} - - from{" "} - {formatDate( - consultationData.last_daily_round.created_at - )} - - - )} -
-
- Consultation Update -
-
- {consultationData.symptoms?.map( - (symptom, index) => ( - choice.id === symptom - )?.text ?? "Err. Unknown" - } - size="small" - /> - ) - )} -
- {consultationData.other_symptoms && ( -
-
- Other Symptoms: -
- {consultationData.other_symptoms} -
- )} - - from{" "} - {consultationData.symptoms_onset_date - ? formatDate(consultationData.symptoms_onset_date) - : "--/--/----"} - -
-
-
- )} - - {consultationData.history_of_present_illness && ( -
-
-

- History of Present Illness -

-
- -
-
-
- )} - - {consultationData.examination_details && ( -
-
-

- Examination details and Clinical conditions:{" "} -

-
- -
-
-
- )} - {consultationData.treatment_plan && ( -
-
-

- Treatment Summary -

-
- -
-
-
- )} - {consultationData.consultation_notes && ( -
-
-

- General Instructions -

-
- -
-
-
- )} - - {(consultationData.operation ?? - consultationData.special_instruction) && ( -
-
-

- Notes -

-
- {consultationData.operation && ( -
-
Operation
- -
- )} - - {consultationData.special_instruction && ( -
-
Special Instruction
- -
- )} -
-
-
- )} -
- {consultationData.procedure && - consultationData.procedure.length > 0 && ( -
-
- - - - - - - - - - - {consultationData.procedure?.map( - (procedure, index) => ( - - - - - - - ) - )} - -
- Procedure - - Notes - - Repetitive - - Time / Frequency -
- {procedure.procedure} - - {procedure.notes} - - {procedure.repetitive ? "Yes" : "No"} - - {procedure.repetitive - ? procedure.frequency - : formatDateTime(String(procedure.time))} -
-
-
- )} - {consultationData.intubation_start_date && ( -
-
-

- Date/Size/LL:{" "} -

-
-
- Intubation Date{" - "} - - {formatDateTime( - consultationData.intubation_start_date - )} - -
-
- Extubation Date{" - "} - - {consultationData.intubation_end_date && - formatDateTime( - consultationData.intubation_end_date - )} - -
-
- ETT/TT (mmid){" - "} - - {consultationData.ett_tt} - -
-
- Cuff Pressure (mmhg){" - "} - - {consultationData.cuff_pressure} - -
-
-
-
- )} - - {consultationData.lines?.length > 0 && ( -
-
-

- Lines and Catheters -

-
- {consultationData.lines?.map( - (line: any, idx: number) => ( -
-
{line.type}
-

- Details: -
- {line.other_type} -

-

- Insertion Date:{" "} - - {formatDateTime(line.start_date)} - -

-

- Site/Level of Fixation:
- - {line.site} - -

-
- ) - )} -
-
-
- )} - -
-
-

- Body Details -

-
-
- Gender {" - "} - - {patientData.gender ?? "-"} - -
-
- Age {" - "} - - {patientData.age ?? "-"} - -
-
- Weight {" - "} - - {consultationData.weight ?? "-"} Kg - -
-
- Height {" - "} - - {consultationData.height ?? "-"} cm - -
-
- Body Surface Area {" - "} - - {Math.sqrt( - (Number(consultationData.weight) * - Number(consultationData.height)) / - 3600 - ).toFixed(2)}{" "} - m2 - -
-
- Blood Group {" - "} - - {patientData.blood_group ?? "-"} - -
-
-
-
-
-
-
- -
- setShowAutomatedRounds((s) => !s)} - /> - -
-
- -
-
-
- )} - {tab === "FEED" && ( -
- - -
- )} - {tab === "SUMMARY" && ( -
- - -
- )} - {tab === "MEDICINES" && ( -
- - -
- )} - {tab === "FILES" && ( -
- -
- )} - - {tab === "ABG" && ( -
- - -
- )} - {tab === "NURSING" && ( -
- - -
- )} - {tab === "NEUROLOGICAL_MONITORING" && ( -
- - -
- )} - {tab === "VENTILATOR" && ( -
- - -
- )} - {tab === "NUTRITION" && ( -
- - -
- )} - {tab === "PRESSURE_SORE" && ( -
- - -
- )} - {tab === "DIALYSIS" && ( -
- - -
- )} - {tab === "INVESTIGATIONS" && ( -
-
- -
- - navigate( - `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/investigation/` - ) - } - > - - {t("log_lab_results")} - -
-
- -
- )} -
- - -
- ); -}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx new file mode 100644 index 00000000000..cf0ce7bd6af --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationABGTab.tsx @@ -0,0 +1,22 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { ABGPlots } from "../Consultations/ABGPlots"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationABGTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx new file mode 100644 index 00000000000..c3515a80953 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationDialysisTab.tsx @@ -0,0 +1,14 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { DialysisPlots } from "../Consultations/DialysisPlots"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationDialysisTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx new file mode 100644 index 00000000000..f4fbc08331c --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx @@ -0,0 +1,22 @@ +import { lazy } from "react"; +import { Feed } from "../Consultations/Feed"; +import { ConsultationTabProps } from "./index"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationFeedTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx new file mode 100644 index 00000000000..754d4bf7b86 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationFilesTab.tsx @@ -0,0 +1,18 @@ +import { ConsultationTabProps } from "./index"; +import { FileUpload } from "../../Patient/FileUpload"; + +export const ConsultationFilesTab = (props: ConsultationTabProps) => { + return ( +
+ +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx new file mode 100644 index 00000000000..69b11a30ed5 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationInvestigationsTab.tsx @@ -0,0 +1,39 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { NonReadOnlyUsers } from "../../../Utils/AuthorizeFor"; +import ButtonV2 from "../../Common/components/ButtonV2"; +import { navigate } from "raviger"; +import CareIcon from "../../../CAREUI/icons/CareIcon"; +import InvestigationTab from "../Investigations/investigationsTab"; +import { t } from "i18next"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); +export const ConsultationInvestigationsTab = (props: ConsultationTabProps) => { + return ( +
+
+ +
+ + navigate( + `/facility/${props.facilityId}/patient/${props.patientId}/consultation/${props.consultationId}/investigation/` + ) + } + > + + {t("log_lab_results")} + +
+
+ +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx new file mode 100644 index 00000000000..19810102833 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationMedicinesTab.tsx @@ -0,0 +1,19 @@ +import { ConsultationTabProps } from "./index"; +import PrescriptionAdministrationsTable from "../../Medicine/PrescriptionAdministrationsTable"; + +export const ConsultationMedicinesTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx new file mode 100644 index 00000000000..85f34f0ec63 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationNeurologicalMonitoringTab.tsx @@ -0,0 +1,24 @@ +import { lazy } from "react"; +import { NeurologicalTable } from "../Consultations/NeurologicalTables"; +import { ConsultationTabProps } from "./index"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationNeurologicalMonitoringTab = ( + props: ConsultationTabProps +) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx new file mode 100644 index 00000000000..69f130aca0d --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationNeutritionTab.tsx @@ -0,0 +1,18 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { NutritionPlots } from "../Consultations/NutritionPlots"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationNeutritionTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx new file mode 100644 index 00000000000..721ee18d13e --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationNursingTab.tsx @@ -0,0 +1,18 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { NursingPlot } from "../Consultations/NursingPlot"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationNursingTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx new file mode 100644 index 00000000000..05327ca6870 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationPressureSoreTab.tsx @@ -0,0 +1,14 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { PressureSoreDiagrams } from "../Consultations/PressureSoreDiagrams"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationPressureSoreTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx new file mode 100644 index 00000000000..69d20dd64c9 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationSummaryTab.tsx @@ -0,0 +1,22 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { PrimaryParametersPlot } from "../Consultations/PrimaryParametersPlot"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationSummaryTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx new file mode 100644 index 00000000000..705b3d9479a --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx @@ -0,0 +1,690 @@ +import { lazy, useEffect, useState } from "react"; +import { ConsultationTabProps } from "./index"; +import { AssetBedModel, AssetClass, AssetData } from "../../Assets/AssetTypes"; +import { useDispatch } from "react-redux"; +import { getPermittedFacility, listAssetBeds } from "../../../Redux/actions"; +import { BedModel, FacilityModel } from "../models"; +import HL7PatientVitalsMonitor from "../../VitalsMonitor/HL7PatientVitalsMonitor"; +import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatientVitalsMonitor"; +import useVitalsAspectRatioConfig from "../../VitalsMonitor/useVitalsAspectRatioConfig"; +import { DISCHARGE_REASONS, SYMPTOM_CHOICES } from "../../../Common/constants"; +import PrescriptionsTable from "../../Medicine/PrescriptionsTable"; +import Chip from "../../../CAREUI/display/Chip"; +import { formatDate, formatDateTime } from "../../../Utils/utils"; +import ReadMore from "../../Common/components/Readmore"; +import { DailyRoundsList } from "../Consultations/DailyRoundsList"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { + const dispatch: any = useDispatch(); + const [showAutomatedRounds, setShowAutomatedRounds] = useState(true); + const [hl7SocketUrl, setHL7SocketUrl] = useState(); + const [ventilatorSocketUrl, setVentilatorSocketUrl] = useState(); + const [monitorBedData, setMonitorBedData] = useState(); + const [ventilatorBedData, setVentilatorBedData] = useState(); + + const vitals = useVitalsAspectRatioConfig({ + default: undefined, + md: 8 / 11, + lg: 15 / 11, + xl: 13 / 11, + "2xl": 19 / 11, + "3xl": 23 / 11, + }); + + useEffect(() => { + if ( + !props.consultationData.facility || + !props.consultationData.current_bed?.bed_object.id + ) + return; + + const fetchData = async () => { + const [facilityRes, assetBedRes] = await Promise.all([ + dispatch(getPermittedFacility(props.consultationData.facility as any)), + dispatch( + listAssetBeds({ + facility: props.consultationData.facility as any, + bed: props.consultationData.current_bed?.bed_object.id, + }) + ), + ]); + + const { middleware_address } = facilityRes.data as FacilityModel; + const assetBeds = assetBedRes?.data?.results as AssetBedModel[]; + + const monitorBedData = assetBeds?.find( + (i) => i.asset_object?.asset_class === AssetClass.HL7MONITOR + ); + setMonitorBedData(monitorBedData); + const assetDataForMonitor = monitorBedData?.asset_object; + const hl7Meta = assetDataForMonitor?.meta; + const hl7Middleware = hl7Meta?.middleware_hostname || middleware_address; + if (hl7Middleware && hl7Meta?.local_ip_address) { + setHL7SocketUrl( + `wss://${hl7Middleware}/observations/${hl7Meta.local_ip_address}` + ); + } + + const consultationBedVentilator = + props.consultationData?.current_bed?.assets_objects?.find( + (i) => i.asset_class === AssetClass.VENTILATOR + ); + let ventilatorBedData; + if (consultationBedVentilator) { + ventilatorBedData = { + asset_object: consultationBedVentilator, + bed_object: props.consultationData?.current_bed?.bed_object, + } as AssetBedModel; + } else { + ventilatorBedData = assetBeds?.find( + (i) => i.asset_object.asset_class === AssetClass.VENTILATOR + ); + } + setVentilatorBedData(ventilatorBedData); + const ventilatorMeta = ventilatorBedData?.asset_object?.meta; + const ventilatorMiddleware = + ventilatorMeta?.middleware_hostname || middleware_address; + if (ventilatorMiddleware && ventilatorMeta?.local_ip_address) { + setVentilatorSocketUrl( + `wss://${ventilatorMiddleware}/observations/${ventilatorMeta?.local_ip_address}` + ); + } + + if ( + !(hl7Middleware && hl7Meta?.local_ip_address) && + !(ventilatorMiddleware && ventilatorMeta?.local_ip_address) + ) { + setHL7SocketUrl(undefined); + setVentilatorSocketUrl(undefined); + } + }; + + fetchData(); + }, [props.consultationData]); + + return ( +
+ {!props.consultationData.discharge_date && + hl7SocketUrl && + ventilatorSocketUrl && ( +
+
+
+ +
+
+ +
+
+
+ )} +
+
+ +
+ {!props.consultationData.discharge_date && + ((hl7SocketUrl && !ventilatorSocketUrl) || + (!hl7SocketUrl && ventilatorSocketUrl)) && ( +
+ {(hl7SocketUrl || ventilatorSocketUrl) && ( +
+ {hl7SocketUrl && ( +
+ +
+ )} + {ventilatorSocketUrl && ( +
+ +
+ )} +
+ )} +
+ )} + {props.consultationData.discharge_date && ( +
+
+

+ Discharge Information +

+
+
+ Reason {" - "} + + {DISCHARGE_REASONS.find( + (d) => + d.id === props.consultationData.discharge_reason + )?.text ?? "--"} + +
+ {props.consultationData.discharge_reason === "REF" && ( +
+ Referred Facility {" - "} + + {props.consultationData.referred_to_external || + props.consultationData.referred_to_object?.name || + "--"} + +
+ )} + {props.consultationData.discharge_reason === "REC" && ( +
+
+ Discharge Date {" - "} + + {props.consultationData.discharge_date + ? formatDate( + props.consultationData.discharge_date + ) + : "--/--/----"} + +
+
+ Advice {" - "} + + {props.consultationData.discharge_notes ?? "--"} + +
+
+ +
+
+
+ +
+
+ )} + {props.consultationData.discharge_reason === "EXP" && ( +
+
+ Date of Death {" - "} + + {props.consultationData.death_datetime + ? formatDateTime( + props.consultationData.death_datetime + ) + : "--:--"} + +
+
+ Cause of death {" - "} + + {props.consultationData.discharge_notes ?? "--"} + +
+
+ Confirmed By {" - "} + + {props.consultationData.death_confirmed_doctor ?? + "--"} + +
+
+ )} + {["REF", "LAMA"].includes( + props.consultationData.discharge_reason ?? "" + ) && ( +
+
+ Discharge Date {" - "} + + {props.consultationData.discharge_date + ? formatDate( + props.consultationData.discharge_date + ) + : "--/--/----"} + +
+
+ Notes {" - "} + + {props.consultationData.discharge_notes ?? "--"} + +
+
+ )} +
+
+
+ )} + {props.consultationData.symptoms_text && ( +
+
+

+ Symptoms +

+
+
+ Last Daily Update +
+ {props.consultationData.last_daily_round + ?.additional_symptoms && ( + <> +
+ {props.consultationData.last_daily_round?.additional_symptoms.map( + (symptom: any, index: number) => ( + choice.id === symptom + )?.text ?? "Err. Unknown" + } + size="small" + /> + ) + )} +
+ {props.consultationData.last_daily_round + ?.other_symptoms && ( +
+
+ Other Symptoms: +
+ { + props.consultationData.last_daily_round + ?.other_symptoms + } +
+ )} + + from{" "} + {formatDate( + props.consultationData.last_daily_round.created_at + )} + + + )} +
+
+ Consultation Update +
+
+ {props.consultationData.symptoms?.map( + (symptom, index) => ( + choice.id === symptom + )?.text ?? "Err. Unknown" + } + size="small" + /> + ) + )} +
+ {props.consultationData.other_symptoms && ( +
+
+ Other Symptoms: +
+ {props.consultationData.other_symptoms} +
+ )} + + from{" "} + {props.consultationData.symptoms_onset_date + ? formatDate(props.consultationData.symptoms_onset_date) + : "--/--/----"} + +
+
+
+ )} + + {props.consultationData.history_of_present_illness && ( +
+
+

+ History of Present Illness +

+
+ +
+
+
+ )} + + {props.consultationData.examination_details && ( +
+
+

+ Examination details and Clinical conditions:{" "} +

+
+ +
+
+
+ )} + {props.consultationData.treatment_plan && ( +
+
+

+ Treatment Summary +

+
+ +
+
+
+ )} + {props.consultationData.consultation_notes && ( +
+
+

+ General Instructions +

+
+ +
+
+
+ )} + + {(props.consultationData.operation ?? + props.consultationData.special_instruction) && ( +
+
+

+ Notes +

+
+ {props.consultationData.operation && ( +
+
Operation
+ +
+ )} + + {props.consultationData.special_instruction && ( +
+
Special Instruction
+ +
+ )} +
+
+
+ )} +
+ {props.consultationData.procedure && + props.consultationData.procedure.length > 0 && ( +
+
+ + + + + + + + + + + {props.consultationData.procedure?.map( + (procedure, index) => ( + + + + + + + ) + )} + +
+ Procedure + + Notes + + Repetitive + + Time / Frequency +
+ {procedure.procedure} + + {procedure.notes} + + {procedure.repetitive ? "Yes" : "No"} + + {procedure.repetitive + ? procedure.frequency + : formatDateTime(String(procedure.time))} +
+
+
+ )} + {props.consultationData.intubation_start_date && ( +
+
+

+ Date/Size/LL:{" "} +

+
+
+ Intubation Date{" - "} + + {formatDateTime( + props.consultationData.intubation_start_date + )} + +
+
+ Extubation Date{" - "} + + {props.consultationData.intubation_end_date && + formatDateTime( + props.consultationData.intubation_end_date + )} + +
+
+ ETT/TT (mmid){" - "} + + {props.consultationData.ett_tt} + +
+
+ Cuff Pressure (mmhg){" - "} + + {props.consultationData.cuff_pressure} + +
+
+
+
+ )} + + {props.consultationData.lines?.length > 0 && ( +
+
+

+ Lines and Catheters +

+
+ {props.consultationData.lines?.map( + (line: any, idx: number) => ( +
+
{line.type}
+

+ Details: +
+ {line.other_type} +

+

+ Insertion Date:{" "} + + {formatDateTime(line.start_date)} + +

+

+ Site/Level of Fixation:
+ + {line.site} + +

+
+ ) + )} +
+
+
+ )} + +
+
+

+ Body Details +

+
+
+ Gender {" - "} + + {props.patientData.gender ?? "-"} + +
+
+ Age {" - "} + + {props.patientData.age ?? "-"} + +
+
+ Weight {" - "} + + {props.consultationData.weight ?? "-"} Kg + +
+
+ Height {" - "} + + {props.consultationData.height ?? "-"} cm + +
+
+ Body Surface Area {" - "} + + {Math.sqrt( + (Number(props.consultationData.weight) * + Number(props.consultationData.height)) / + 3600 + ).toFixed(2)}{" "} + m2 + +
+
+ Blood Group {" - "} + + {props.patientData.blood_group ?? "-"} + +
+
+
+
+
+
+
+ +
+ setShowAutomatedRounds((s) => !s)} + /> + +
+
+ +
+
+
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx new file mode 100644 index 00000000000..d14b54cc096 --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/ConsultationVentilatorTab.tsx @@ -0,0 +1,22 @@ +import { lazy } from "react"; +import { ConsultationTabProps } from "./index"; +import { VentilatorPlot } from "../Consultations/VentilatorPlot"; + +const PageTitle = lazy(() => import("../../Common/PageTitle")); + +export const ConsultationVentilatorTab = (props: ConsultationTabProps) => { + return ( +
+ + +
+ ); +}; diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx new file mode 100644 index 00000000000..affdb2756cd --- /dev/null +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -0,0 +1,501 @@ +import { + CONSULTATION_TABS, + GENDER_TYPES, + OptionsType, + SYMPTOM_CHOICES, +} from "../../../Common/constants"; +import { ConsultationModel, ICD11DiagnosisModel } from "../models"; +import { getConsultation, getPatient } from "../../../Redux/actions"; +import { statusType, useAbortableEffect } from "../../../Common/utils"; +import { lazy, useCallback, useState } from "react"; + +import ButtonV2 from "../../Common/components/ButtonV2"; +import CareIcon from "../../../CAREUI/icons/CareIcon"; +import DischargeModal from "../DischargeModal"; +import DischargeSummaryModal from "../DischargeSummaryModal"; +import DoctorVideoSlideover from "../DoctorVideoSlideover"; +import { make as Link } from "../../Common/components/Link.bs"; +import PatientInfoCard from "../../Patient/PatientInfoCard"; +import { PatientModel } from "../../Patient/models"; +import { formatDateTime, relativeTime } from "../../../Utils/utils"; + +import { navigate } from "raviger"; +import { useDispatch } from "react-redux"; +import { useQueryParams } from "raviger"; +import { useTranslation } from "react-i18next"; +import { triggerGoal } from "../../Common/Plausible"; +import useAuthUser from "../../../Common/hooks/useAuthUser"; +import { ConsultationUpdatesTab } from "./ConsultationUpdatesTab"; +import { ConsultationABGTab } from "./ConsultationABGTab"; +import { ConsultationNursingTab } from "./ConsultationNursingTab"; +import { ConsultationFeedTab } from "./ConsultationFeedTab"; +import { ConsultationSummaryTab } from "./ConsultationSummaryTab"; +import { ConsultationFilesTab } from "./ConsultationFilesTab"; +import { ConsultationMedicinesTab } from "./ConsultationMedicinesTab"; +import { ConsultationInvestigationsTab } from "./ConsultationInvestigationsTab"; +import { ConsultationVentilatorTab } from "./ConsultationVentilatorTab"; +import { ConsultationPressureSoreTab } from "./ConsultationPressureSoreTab"; +import { ConsultationDialysisTab } from "./ConsultationDialysisTab"; +import { ConsultationNeurologicalMonitoringTab } from "./ConsultationNeurologicalMonitoringTab"; + +const Loading = lazy(() => import("../../Common/Loading")); +const PageTitle = lazy(() => import("../../Common/PageTitle")); +const symptomChoices = [...SYMPTOM_CHOICES]; + +export interface ConsultationTabProps { + consultationId: string; + facilityId: string; + patientId: string; + consultationData: ConsultationModel; + patientData: PatientModel; +} + +export const ConsultationDetails = (props: any) => { + const { t } = useTranslation(); + const { facilityId, patientId, consultationId } = props; + const tab = props.tab.toUpperCase(); + const dispatch: any = useDispatch(); + const [isLoading, setIsLoading] = useState(false); + const [showDoctors, setShowDoctors] = useState(false); + const [qParams, _] = useQueryParams(); + + const [consultationData, setConsultationData] = useState( + {} as ConsultationModel + ); + const [patientData, setPatientData] = useState({}); + const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] = + useState(false); + const [openDischargeDialog, setOpenDischargeDialog] = useState(false); + + const getPatientGender = (patientData: any) => + GENDER_TYPES.find((i) => i.id === patientData.gender)?.text; + + const getPatientAddress = (patientData: any) => + `${patientData.address},\n${patientData.ward_object?.name},\n${patientData.local_body_object?.name},\n${patientData.district_object?.name},\n${patientData.state_object?.name}`; + + const getPatientComorbidities = (patientData: any) => { + if (patientData?.medical_history?.length) { + const medHis = patientData.medical_history; + return medHis.map((item: any) => item.disease).join(", "); + } else { + return "None"; + } + }; + + const authUser = useAuthUser(); + + const fetchData = useCallback( + async (status: statusType) => { + setIsLoading(true); + const res = await dispatch(getConsultation(consultationId)); + if (!status.aborted) { + if (res?.data) { + const data: ConsultationModel = { + ...res.data, + symptoms_text: "", + }; + if (res.data.symptoms?.length) { + const symptoms = res.data.symptoms + .filter((symptom: number) => symptom !== 9) + .map((symptom: number) => { + const option = symptomChoices.find((i) => i.id === symptom); + return option ? option.text.toLowerCase() : symptom; + }); + data.symptoms_text = symptoms.join(", "); + } + setConsultationData(data); + const id = res.data.patient; + const patientRes = await dispatch(getPatient({ id })); + if (patientRes?.data) { + const patientGender = getPatientGender(patientRes.data); + const patientAddress = getPatientAddress(patientRes.data); + const patientComorbidities = getPatientComorbidities( + patientRes.data + ); + const data = { + ...patientRes.data, + gender: patientGender, + address: patientAddress, + comorbidities: patientComorbidities, + is_declared_positive: patientRes.data.is_declared_positive + ? "Yes" + : "No", + is_vaccinated: patientData.is_vaccinated ? "Yes" : "No", + }; + setPatientData(data); + } + } else { + navigate("/not-found"); + } + setIsLoading(false); + } + }, + [consultationId, dispatch, patientData.is_vaccinated] + ); + + useAbortableEffect((status: statusType) => { + fetchData(status); + triggerGoal("Patient Consultation Viewed", { + facilityId: facilityId, + consultationId: consultationId, + userId: authUser.id, + }); + }, []); + + const TABS = { + UPDATES: ConsultationUpdatesTab, + FEED: ConsultationFeedTab, + SUMMARY: ConsultationSummaryTab, + MEDICINES: ConsultationMedicinesTab, + FILES: ConsultationFilesTab, + INVESTIGATIONS: ConsultationInvestigationsTab, + ABG: ConsultationABGTab, + NURSING: ConsultationNursingTab, + NEUROLOGICAL_MONITORING: ConsultationNeurologicalMonitoringTab, + VENTILATOR: ConsultationVentilatorTab, + NUTRITION: ConsultationNursingTab, + PRESSURE_SORE: ConsultationPressureSoreTab, + DIALYSIS: ConsultationDialysisTab, + }; + + const consultationTabProps: ConsultationTabProps = { + consultationId, + facilityId, + patientId, + consultationData, + patientData, + }; + + const SelectedTab = TABS[tab]; + + if (isLoading) { + return ; + } + + const tabButtonClasses = (selected: boolean) => + `capitalize min-w-max-content cursor-pointer border-transparent text-gray-700 hover:text-gray-700 hover:border-gray-300 font-bold whitespace-nowrap ${ + selected === true ? "border-primary-500 text-primary-600 border-b-2" : "" + }`; + + const ShowDiagnosis = ({ + diagnoses = [], + label = "Diagnosis", + nshow = 2, + }: { + diagnoses: ICD11DiagnosisModel[] | undefined; + label: string; + nshow?: number; + }) => { + const [showMore, setShowMore] = useState(false); + + return diagnoses.length ? ( +
+

{label}

+ + {diagnoses.slice(0, !showMore ? nshow : undefined).map((diagnosis) => ( +

{diagnosis.label}

+ ))} + {diagnoses.length > nshow && ( + <> + {!showMore ? ( + setShowMore(true)} + className="cursor-pointer text-sm text-blue-600 hover:text-blue-300" + > + show more + + ) : ( + setShowMore(false)} + className="cursor-pointer text-sm text-blue-600 hover:text-blue-300" + > + show less + + )} + + )} +
+ ) : null; + }; + + return ( +
+ setOpenDischargeSummaryDialog(false)} + /> + + setOpenDischargeDialog(false)} + consultationData={consultationData} + /> + +
+ +
+
+ + +
+ {consultationData.admitted_to && ( +
+
+ Patient + {consultationData.discharge_date + ? " Discharged from" + : " Admitted to"} + + {consultationData.admitted_to} + +
+ {(consultationData.admission_date ?? + consultationData.discharge_date) && ( +
+ {relativeTime( + consultationData.discharge_date + ? consultationData.discharge_date + : consultationData.admission_date + )} +
+ )} +
+ {consultationData.admission_date && + formatDateTime(consultationData.admission_date)} + {consultationData.discharge_date && + ` - ${formatDateTime(consultationData.discharge_date)}`} +
+
+ )} +
+ +
+
+ {/*consultationData.other_symptoms && ( +
+ + Other Symptoms:{" "} + + {consultationData.other_symptoms} +
+ )*/} + + {consultationData.icd11_principal_diagnosis && ( + + d.id === consultationData.icd11_principal_diagnosis + )!, + ]} + /> + )} + + + + + + {(consultationData.verified_by_object || + consultationData.deprecated_verified_by) && ( +
+ + Verified By:{" "} + + {consultationData.verified_by_object + ? `${consultationData.verified_by_object.first_name} ${consultationData.verified_by_object.last_name}` + : consultationData.deprecated_verified_by} + +
+ )} +
+
+ setOpenDischargeSummaryDialog(true)}> + + {t("discharge_summary")} + + + setOpenDischargeDialog(true)} + disabled={!!consultationData.discharge_date} + > + + {t("discharge_from_care")} + +
+
+
+
+
+ Created: + {consultationData.created_date + ? formatDateTime(consultationData.created_date) + : "--:--"}{" "} + | +
+ {consultationData.created_by && ( +
+ {` ${consultationData.created_by.first_name} ${consultationData.created_by.last_name} `} + {`@${consultationData.created_by.username} (${consultationData.created_by.user_type})`} +
+ )} +
+
+
+ Last Modified: + {consultationData.modified_date + ? formatDateTime(consultationData.modified_date) + : "--:--"}{" "} + | +
+ {consultationData.last_edited_by && ( +
+ {` ${consultationData.last_edited_by.first_name} ${consultationData.last_edited_by.last_name} `} + {`@${consultationData.last_edited_by.username} (${consultationData.last_edited_by.user_type})`} +
+ )} +
+
+
+
+ +
+
+
+ +
+
+
+ + +
+ + +
+ ); +}; From ac9a4ee6a60bce858e0eaa56a0f47380f8d255df Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:19:44 +0530 Subject: [PATCH 22/50] Add validation for average working hours (#6243) * Add validation for average working hours * Fix error message --- src/Components/Users/ManageUsers.tsx | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx index d2e0ca415e3..8625d442f52 100644 --- a/src/Components/Users/ManageUsers.tsx +++ b/src/Components/Users/ManageUsers.tsx @@ -73,6 +73,8 @@ export default function ManageUsers() { name: string; }>({ show: false, username: "", name: "" }); + const [weeklyHoursError, setWeeklyHoursError] = useState(""); + const extremeSmallScreenBreakpoint = 320; const isExtremeSmallScreen = width <= extremeSmallScreenBreakpoint ? true : false; @@ -144,7 +146,10 @@ export default function ManageUsers() { const handleWorkingHourSubmit = async () => { const username = selectedUser; - if (!username || weeklyHours < 0 || weeklyHours > 168) return; + if (!username || !weeklyHours || weeklyHours < 0 || weeklyHours > 168) { + setWeeklyHoursError("Value should be between 0 and 168"); + return; + } const res = await dispatch( partialUpdateUser(username, { weekly_working_hours: weeklyHours, @@ -163,6 +168,7 @@ export default function ManageUsers() { }); } setWeeklyHours(0); + setWeeklyHoursError(""); fetchData({ aborted: false }); }; @@ -493,13 +499,14 @@ export default function ManageUsers() { { + setExpandWorkingHours(state); + setWeeklyHours(0); + setWeeklyHoursError(""); + }} slideFrom="right" title="Average weekly working hours" dialogClass="md:w-[400px]" - onCloseClick={() => { - setWeeklyHours(0); - }} >
@@ -512,11 +519,7 @@ export default function ManageUsers() { onChange={(e) => { setWeeklyHours(e.value); }} - error={ - weeklyHours < 0 || weeklyHours > 168 - ? "Average weekly working hours should be between 0 and 168" - : "" - } + error={weeklyHoursError} required label="" type="number" From 3d73c69b4d9f8ca094bbc93407274a3b80da53a6 Mon Sep 17 00:00:00 2001 From: Kshitij Verma <101321276+kshitijv256@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:20:41 +0530 Subject: [PATCH 23/50] changed priority of backUrl and history for back button in pageTitle (#6251) --- src/Common/hooks/useAppHistory.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Common/hooks/useAppHistory.ts b/src/Common/hooks/useAppHistory.ts index 893b1a06cfd..70ad92692de 100644 --- a/src/Common/hooks/useAppHistory.ts +++ b/src/Common/hooks/useAppHistory.ts @@ -10,14 +10,12 @@ export default function useAppHistory() { const resetHistory = useContext(ResetHistoryContext); const goBack = (fallbackUrl?: string) => { - if (history.length > 1) - // Navigate to history present in the app navigation history stack. - return navigate(history[1]); - if (fallbackUrl) - // Otherwise, use provided fallback url if provided. + // use provided fallback url if provided. return navigate(fallbackUrl); - + if (history.length > 1) + // Otherwise, navigate to history present in the app navigation history stack. + return navigate(history[1]); // Otherwise, fallback to browser's go back behaviour. window.history.back(); }; From baecaab94b617302fb20cf4aa7766325defb8870 Mon Sep 17 00:00:00 2001 From: print-Sathvik <113630200+print-Sathvik@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:21:27 +0530 Subject: [PATCH 24/50] Asset filter parameters reset on unselect (#6255) --- src/Components/Assets/AssetFilter.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Components/Assets/AssetFilter.tsx b/src/Components/Assets/AssetFilter.tsx index b0a870d0e31..299d67e5ab0 100644 --- a/src/Components/Assets/AssetFilter.tsx +++ b/src/Components/Assets/AssetFilter.tsx @@ -108,9 +108,9 @@ function AssetFilter(props: any) { const applyFilter = () => { const data = { facility: facilityId, - asset_type: asset_type, - asset_class: asset_class, - status: asset_status, + asset_type: asset_type ?? "", + asset_class: asset_class ?? "", + status: asset_status ?? "", location: locationId, }; onChange(data); From 4e8005c96bda7baa03442d21f5b18bb592426d59 Mon Sep 17 00:00:00 2001 From: Pranshu Aggarwal <70687348+Pranshu1902@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:23:25 +0530 Subject: [PATCH 25/50] Make the form consistent (#6238) * shift right * Update src/Components/Facility/AddLocationForm.tsx Co-authored-by: Rithvik Nishad --------- Co-authored-by: Rithvik Nishad --- src/Components/Facility/AddLocationForm.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Facility/AddLocationForm.tsx b/src/Components/Facility/AddLocationForm.tsx index d3c64ec9e04..f0dd7893aca 100644 --- a/src/Components/Facility/AddLocationForm.tsx +++ b/src/Components/Facility/AddLocationForm.tsx @@ -158,7 +158,7 @@ export const AddLocationForm = (props: LocationFormProps) => { />
-
+
navigate(`/facility/${facilityId}/location`, { From 7cda789169e188d377134596aaae81731a523acd Mon Sep 17 00:00:00 2001 From: "Tasnimul H. Tauhid" Date: Mon, 11 Sep 2023 19:25:00 +0530 Subject: [PATCH 26/50] Disabled asset config for user other than admin (#6259) --- src/Components/Assets/AssetManage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Components/Assets/AssetManage.tsx b/src/Components/Assets/AssetManage.tsx index 825cee1c848..25f879ddd09 100644 --- a/src/Components/Assets/AssetManage.tsx +++ b/src/Components/Assets/AssetManage.tsx @@ -29,7 +29,7 @@ import { useTranslation } from "react-i18next"; const PageTitle = lazy(() => import("../Common/PageTitle")); const Loading = lazy(() => import("../Common/Loading")); import * as Notification from "../../Utils/Notifications.js"; -import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; +import AuthorizeFor, { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; import Uptime from "../Common/Uptime"; import useAuthUser from "../../Common/hooks/useAuthUser"; import dayjs from "dayjs"; @@ -452,7 +452,7 @@ const AssetManage = (props: AssetManageProps) => { } id="configure-asset" data-testid="asset-configure-button" - authorizeFor={NonReadOnlyUsers} + authorizeFor={AuthorizeFor(["DistrictAdmin", "StateAdmin"])} > {t("configure")} From 2da1eeeee8788e6c38d5ba1a590da8cd0ee3ae50 Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Tue, 12 Sep 2023 09:36:39 +0530 Subject: [PATCH 27/50] add test to search asset by qr and serial number (#6266) * add search by qr and ID * made variables values dynamic --- .../{asset_tab.cy.ts => asset_homepage.cy.ts} | 28 +++++++++-- cypress/pageobject/Asset/AssetSearch.ts | 47 +++++++++++++++++-- 2 files changed, 65 insertions(+), 10 deletions(-) rename cypress/e2e/assets_spec/{asset_tab.cy.ts => asset_homepage.cy.ts} (55%) diff --git a/cypress/e2e/assets_spec/asset_tab.cy.ts b/cypress/e2e/assets_spec/asset_homepage.cy.ts similarity index 55% rename from cypress/e2e/assets_spec/asset_tab.cy.ts rename to cypress/e2e/assets_spec/asset_homepage.cy.ts index 0abf11fcaf0..6e9ceb9676b 100644 --- a/cypress/e2e/assets_spec/asset_tab.cy.ts +++ b/cypress/e2e/assets_spec/asset_homepage.cy.ts @@ -5,15 +5,21 @@ import { AssetSearchPage } from "../../pageobject/Asset/AssetSearch"; import { AssetQRScanPage } from "../../pageobject/Asset/AssetQRScan"; import { AssetPagination } from "../../pageobject/Asset/AssetPagination"; import { AssetFilters } from "../../pageobject/Asset/AssetFilters"; +import LoginPage from "../../pageobject/Login/LoginPage"; +import { v4 as uuidv4 } from "uuid"; describe("Asset Tab", () => { const assetSearchPage = new AssetSearchPage(); const assetQRScanPage = new AssetQRScanPage(); const assetPagination = new AssetPagination(); const assetFilters = new AssetFilters(); + const loginPage = new LoginPage(); + const assetName = "Dummy Camera 10"; + const qrCode = uuidv4(); + const serialNumber = Math.floor(Math.random() * 10 ** 10).toString(); before(() => { - cy.loginByApi("devdistrictadmin", "Coronasafe@123"); + loginPage.loginAsDisctrictAdmin(); cy.saveLocalStorage(); }); @@ -24,11 +30,23 @@ describe("Asset Tab", () => { // search for a element - it("Search Asset Name", () => { - const initialUrl = cy.url(); - assetSearchPage.typeSearchKeyword("dummy camera 30"); + it("Search Asset Name/QR_ID/Serial_number", () => { + assetSearchPage.typeSearchKeyword(assetName); assetSearchPage.pressEnter(); - assetSearchPage.verifyUrlChanged(initialUrl); + assetSearchPage.verifyBadgeContent(assetName); + assetSearchPage.clickAssetByName(assetName); + assetSearchPage.clickUpdateButton(); + assetSearchPage.clearAndTypeQRCode(qrCode); + assetSearchPage.clearAndTypeSerialNumber(serialNumber); + assetSearchPage.clickAssetSubmitButton(); + assetSearchPage.visitAssetsPage(); + assetSearchPage.typeSearchKeyword(qrCode); + assetSearchPage.pressEnter(); + assetSearchPage.verifyAssetListContains(assetName); + assetSearchPage.verifyBadgeContent(qrCode); + assetSearchPage.typeSearchKeyword(serialNumber); + assetSearchPage.verifyAssetListContains(assetName); + assetSearchPage.verifyBadgeContent(serialNumber); }); // scan a asset qr code diff --git a/cypress/pageobject/Asset/AssetSearch.ts b/cypress/pageobject/Asset/AssetSearch.ts index 315a414a62b..fea6a975983 100644 --- a/cypress/pageobject/Asset/AssetSearch.ts +++ b/cypress/pageobject/Asset/AssetSearch.ts @@ -1,16 +1,22 @@ export class AssetSearchPage { typeSearchKeyword(keyword: string) { - cy.get("[name='search']").type(keyword); + cy.get("#search").clear(); + cy.get("#search").click().type(keyword); } pressEnter() { cy.get("[name='search']").type("{enter}"); } - verifyUrlChanged(initialUrl: string) { - cy.url().should((currentUrl) => { - expect(currentUrl).not.to.equal(initialUrl); - }); + clickAssetByName(assetName: string) { + cy.get("[data-testid='created-asset-list']").contains(assetName).click(); + } + + verifyBadgeContent(expectedText: string) { + cy.get("[data-testid='Name/Serial No./QR ID']").should( + "contain", + expectedText + ); } verifyAssetIsPresent(assetName: string) { @@ -18,4 +24,35 @@ export class AssetSearchPage { .first() .should("contain", assetName); } + + clickUpdateButton() { + cy.get("[data-testid='asset-update-button']").contains("Update").click(); + } + + clearAndTypeQRCode(qrCode: string) { + cy.get("#qr_code_id").clear(); + cy.get("#qr_code_id").click().type(qrCode); + } + + clearAndTypeSerialNumber(serialNumber: string) { + cy.get("#serial-number").clear(); + cy.get("#serial-number").click().type(serialNumber); + } + + clickAssetSubmitButton() { + cy.intercept("GET", "**/api/v1/asset/**").as("getAssets"); + cy.get("#submit").click(); + cy.wait("@getAssets").its("response.statusCode").should("eq", 200); + } + + visitAssetsPage() { + cy.visit("/assets"); + } + + verifyAssetListContains(dummyCameraText: string) { + cy.get("[data-testid='created-asset-list']").should( + "contain", + dummyCameraText + ); + } } From 203288eaae5392cb4ad4bfd49205233efa1119c2 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Tue, 12 Sep 2023 10:27:31 +0530 Subject: [PATCH 28/50] remove quotes from `.env`, gitignore bun lock file (#6263) --- .env | 8 ++++---- .gitignore | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.env b/.env index fcabed6db34..61d4177305c 100644 --- a/.env +++ b/.env @@ -1,11 +1,11 @@ # Whitelabelling envs -REACT_APP_TITLE="CARE" -REACT_APP_META_DESCRIPTION="CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations." +REACT_APP_TITLE=CARE +REACT_APP_META_DESCRIPTION=CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations. REACT_APP_COVER_IMAGE=https://cdn.coronasafe.network/care_logo.svg REACT_APP_COVER_IMAGE_ALT=https://cdn.coronasafe.network/care_logo.svg -REACT_APP_CONFIG="" -REACT_PUBLIC_URL="https://care.coronasafe.in" +REACT_APP_CONFIG= +REACT_PUBLIC_URL=https://care.coronasafe.in # Dev envs ESLINT_NO_DEV_ERRORS=true diff --git a/.gitignore b/.gitignore index b97356ed20f..4ca589aab39 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ public/build-meta.json # Using NPM yarn.lock pnpm-lock.yaml +bun.lockb # Cypress cypress/downloads From f7ac3338956bff973afeeec5c4ec6e8eb5eca35d Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Tue, 12 Sep 2023 10:39:11 +0530 Subject: [PATCH 29/50] remove `REACT_APP_CONFIG` from `.env` (#6268) --- .env | 1 - src/Redux/api.tsx | 2 +- src/vite-env.d.ts | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.env b/.env index 61d4177305c..38fd950b0d8 100644 --- a/.env +++ b/.env @@ -4,7 +4,6 @@ REACT_APP_TITLE=CARE REACT_APP_META_DESCRIPTION=CoronaSafe Network is an open-source public utility designed by a multi-disciplinary team of innovators and volunteers. CoronaSafe Care is a Digital Public Good recognised by United Nations. REACT_APP_COVER_IMAGE=https://cdn.coronasafe.network/care_logo.svg REACT_APP_COVER_IMAGE_ALT=https://cdn.coronasafe.network/care_logo.svg -REACT_APP_CONFIG= REACT_PUBLIC_URL=https://care.coronasafe.in # Dev envs diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index 393b9372ca5..01a91d4ce3a 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -10,7 +10,7 @@ interface Routes { const routes: Routes = { config: { - path: import.meta.env.REACT_APP_CONFIG || "/config.json", + path: import.meta.env.REACT_APP_CONFIG ?? "/config.json", method: "GET", noAuth: true, }, diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 529c2689aed..c899d453626 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -5,7 +5,7 @@ interface ImportMetaEnv { readonly REACT_APP_META_DESCRIPTION: string; readonly REACT_APP_COVER_IMAGE: string; readonly REACT_APP_COVER_IMAGE_ALT: string; - readonly REACT_APP_CONFIG: string; + readonly REACT_APP_CONFIG: string | undefined; readonly REACT_PUBLIC_URL: string; readonly REACT_APP_SITE_URL: string; readonly REACT_APP_ANALYTICS_SERVER_URL: string; From dd1dcc3e5af7cdf9ced3b71c264614d78614243a Mon Sep 17 00:00:00 2001 From: Aakash Singh Date: Tue, 12 Sep 2023 15:16:48 +0530 Subject: [PATCH 30/50] fix load dummy data command (#6270) * fix load dummy data command * fix --- .github/workflows/cypress.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index 4f23fbe4937..f66f9a37bd8 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -25,12 +25,13 @@ jobs: path: care - name: Run docker compose up on care ๐Ÿณ - run: cd care && touch .env && make docker_config_file=docker-compose.pre-built.yaml up && cd .. && sleep 60s - # Voluntarily kept 60 seconds delay to wait for migrations to complete. - - - name: Run Django collectstatic and load dummy data on care ๐Ÿ run: | - docker exec care python manage.py load_dummy_data + cd care + make docker_config_file=docker-compose.pre-built.yaml up + sleep 60s + docker compose exec backend bash -c "python manage.py load_dummy_data" + cd .. + # Voluntarily kept 60 seconds delay to wait for migrations to complete. - name: Check care is up โ™ป run: curl -o /dev/null -s -w "%{http_code}\n" http://localhost:9000 From d0ffbd5955f34755d077ff8d57e2e5c22ceafe8b Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Tue, 12 Sep 2023 17:40:58 +0530 Subject: [PATCH 31/50] Refactor workflow to properly wait for migrations (#6272) * Refactor workflow to properly wait for migrations * use while * Fix command and remove cypress videos --- .github/workflows/cypress.yaml | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index f66f9a37bd8..130f360d2ef 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -24,14 +24,22 @@ jobs: repository: coronasafe/care path: care - - name: Run docker compose up on care ๐Ÿณ + - name: Start care docker containers ๐Ÿณ run: | - cd care + cd care make docker_config_file=docker-compose.pre-built.yaml up - sleep 60s + while docker compose exec backend bash -c "python manage.py showmigrations 2>/dev/null | cat | grep -q '\[ \]'"; do + >&2 echo "Migrations are not yet applied - sleeping" + sleep 5 + done + echo "Migrations are applied" + cd .. + + - name: Load dummy data into care backend ๐Ÿ“‚ + run: | + cd care docker compose exec backend bash -c "python manage.py load_dummy_data" cd .. - # Voluntarily kept 60 seconds delay to wait for migrations to complete. - name: Check care is up โ™ป run: curl -o /dev/null -s -w "%{http_code}\n" http://localhost:9000 @@ -93,10 +101,3 @@ jobs: name: cypress-screenshots path: cypress/screenshots - # Test run video was always captured, so this action uses "always()" condition - - name: Upload cypress videos ๐Ÿ“น - uses: actions/upload-artifact@v3 - if: always() - with: - name: cypress-videos - path: cypress/videos From 8c242d650395990170ef5ee21b8a104c89bf4f51 Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Tue, 12 Sep 2023 19:21:04 +0530 Subject: [PATCH 32/50] Asset CSV export (#6262) --- src/Common/constants.tsx | 2 +- src/Components/Assets/AssetsList.tsx | 20 ++++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index e8ab4867764..72a939b92fd 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -993,7 +993,7 @@ export const XLSXAssetImportSchema = { return ip; }, }, - "Config: Camera Access Key": { + "Config - Camera Access Key": { prop: "camera_access_key", type: String, }, diff --git a/src/Components/Assets/AssetsList.tsx b/src/Components/Assets/AssetsList.tsx index 6183a47783c..3789d7cfd51 100644 --- a/src/Components/Assets/AssetsList.tsx +++ b/src/Components/Assets/AssetsList.tsx @@ -324,7 +324,7 @@ const AssetsList = () => { }, }, { - label: "Export Assets", + label: "Export Assets (JSON)", action: () => authorizedForImportExport && listAssets({ @@ -333,7 +333,23 @@ const AssetsList = () => { limit: totalCount, }), type: "json", - filePrefix: `assets_${facility?.name}`, + filePrefix: `assets_${facility?.name ?? "all"}`, + options: { + icon: , + disabled: totalCount === 0 || !authorizedForImportExport, + }, + }, + { + label: "Export Assets (CSV)", + action: () => + authorizedForImportExport && + listAssets({ + ...qParams, + csv: true, + limit: totalCount, + }), + type: "csv", + filePrefix: `assets_${facility?.name ?? "all"}`, options: { icon: , disabled: totalCount === 0 || !authorizedForImportExport, From 87f36ba0332f69fce09ef08dc182449513fa2059 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Tue, 12 Sep 2023 19:24:12 +0530 Subject: [PATCH 33/50] add readmission (#6239) --- src/Components/Facility/ConsultationCard.tsx | 10 ++++++++++ src/Components/Facility/models.tsx | 1 + src/Components/Patient/ManagePatients.tsx | 9 +++++++++ src/Components/Patient/PatientInfoCard.tsx | 17 +++++++++++++---- 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/Components/Facility/ConsultationCard.tsx b/src/Components/Facility/ConsultationCard.tsx index 467729f0f73..f6b4484b477 100644 --- a/src/Components/Facility/ConsultationCard.tsx +++ b/src/Components/Facility/ConsultationCard.tsx @@ -5,6 +5,7 @@ import ButtonV2 from "../Common/components/ButtonV2"; import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; import RelativeDateUserMention from "../Common/RelativeDateUserMention"; import useConfig from "../../Common/hooks/useConfig"; +import Chip from "../../CAREUI/display/Chip"; interface ConsultationProps { itemData: ConsultationModel; @@ -70,6 +71,15 @@ export const ConsultationCard = (props: ConsultationProps) => {
{formatDateTime(itemData.admission_date)} + {itemData.is_readmission && ( + + )}
diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index 012ca1d68b2..cc34140e7a9 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -142,6 +142,7 @@ export interface ConsultationModel { cause_of_death?: string; death_datetime?: string; death_confirmed_doctor?: string; + is_readmission?: boolean; } export interface PatientStatsModel { id?: number; diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx index b29c112c5a1..54d22b5f54c 100644 --- a/src/Components/Patient/ManagePatients.tsx +++ b/src/Components/Patient/ManagePatients.tsx @@ -603,6 +603,15 @@ export const PatientManager = () => { text="Review Missed" /> )} + {patient.last_consultation?.is_readmission && ( + + )} {patient.disease_status === "POSITIVE" && ( )}
-
+
โ€ข - - Domiciliary Care{" "} - + + + Domiciliary Care + + + )} + {consultation?.is_readmission && ( + <> + โ€ข + + + Readmitted )} From 3fdf1d503d7f2ad7e46e03e5fc0e1bf87bf26426 Mon Sep 17 00:00:00 2001 From: "Tasnimul H. Tauhid" Date: Tue, 12 Sep 2023 19:25:19 +0530 Subject: [PATCH 34/50] Add Consultation button is now conditionally active (#6260) * Add Consultation button is now conditionally active * updated logic for add consultation button to be active --- src/Components/Patient/PatientHome.tsx | 31 +++++++++++++++++++------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Components/Patient/PatientHome.tsx b/src/Components/Patient/PatientHome.tsx index 34e5ed1cb01..3d39f377462 100644 --- a/src/Components/Patient/PatientHome.tsx +++ b/src/Components/Patient/PatientHome.tsx @@ -374,6 +374,13 @@ export const PatientHome = (props: any) => { ); }; + const isPatientEligibleForNewConsultation = (patientData: PatientModel) => { + return !patientData.last_consultation || + patientData.last_consultation?.discharge_date + ? true + : false; + }; + return ( {
- patientData.is_active && - (!patientData?.last_consultation || - patientData?.last_consultation?.discharge_date) && + isPatientEligibleForNewConsultation(patientData) && navigate( `/facility/${patientData?.facility}/patient/${id}/consultation` ) } > -
-
+
+
From 8a704af2abe62a73cfa4e27d80fc0c573c30163e Mon Sep 17 00:00:00 2001 From: yaswanth sai vendra <75136628+yaswanthsaivendra@users.noreply.github.com> Date: Tue, 12 Sep 2023 19:26:13 +0530 Subject: [PATCH 35/50] Added None option in admitted_to bed filters so that we can filter for patient who are admitted but no bed assigned (#5550) * added None option in admitted_to bed filters * changed the id from 8 to None * make text user friendly --------- Co-authored-by: Rithvik Nishad --- src/Common/constants.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 72a939b92fd..f5801e97e4b 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -345,6 +345,7 @@ export const ADMITTED_TO = [ { id: "2", text: "ICU" }, { id: "6", text: "Bed with oxygen support" }, { id: "7", text: "Regular" }, + { id: "None", text: "No bed assigned" }, ]; export const RESPIRATORY_SUPPORT = [ From f72a1cf12f6ba80bf03c1df6ce5cfb2c90fc150c Mon Sep 17 00:00:00 2001 From: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Date: Wed, 13 Sep 2023 07:49:18 +0530 Subject: [PATCH 36/50] verify asset filter (#6275) --- cypress/e2e/assets_spec/asset_homepage.cy.ts | 12 +++++- cypress/pageobject/Asset/AssetFilters.ts | 39 +++++++++++++++++++- cypress/pageobject/Asset/AssetSearch.ts | 4 +- src/CAREUI/interactive/SlideOver.tsx | 1 + src/Components/Assets/AssetFilter.tsx | 2 +- 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/cypress/e2e/assets_spec/asset_homepage.cy.ts b/cypress/e2e/assets_spec/asset_homepage.cy.ts index 6e9ceb9676b..fa24adf5b21 100644 --- a/cypress/e2e/assets_spec/asset_homepage.cy.ts +++ b/cypress/e2e/assets_spec/asset_homepage.cy.ts @@ -62,8 +62,18 @@ describe("Asset Tab", () => { "Dummy Facility 1", "INTERNAL", "ACTIVE", - "ONVIF Camera" + "ONVIF Camera", + "Camera Loc" ); + assetFilters.clickadvancefilter(); + assetFilters.clickslideoverbackbutton(); // to verify the back button doesn't clear applied filters + assetFilters.assertFacilityText("Dummy Facility 1"); + assetFilters.assertAssetTypeText("INTERNAL"); + assetFilters.assertAssetClassText("ONVIF"); + assetFilters.assertStatusText("ACTIVE"); + assetFilters.assertLocationText("Camera Locations"); + assetFilters.clickadvancefilter(); + assetFilters.clearFilters(); }); // Verify the pagination in the page diff --git a/cypress/pageobject/Asset/AssetFilters.ts b/cypress/pageobject/Asset/AssetFilters.ts index 57cc893bdb7..a16b61f4fc5 100644 --- a/cypress/pageobject/Asset/AssetFilters.ts +++ b/cypress/pageobject/Asset/AssetFilters.ts @@ -3,7 +3,8 @@ export class AssetFilters { facilityName: string, assetType: string, assetStatus: string, - assetClass: string + assetClass: string, + assetLocation: string ) { cy.contains("Advanced Filters").click(); cy.get("input[name='Facilities']") @@ -27,6 +28,42 @@ export class AssetFilters { .then(() => { cy.get("[role='option']").contains(assetClass).click(); }); + cy.get("#Facilities-location") + .click() + .type(assetLocation) + .then(() => { + cy.get("[role='option']").contains(assetLocation).click(); + }); cy.contains("Apply").click(); } + clearFilters() { + cy.intercept("GET", "**/api/v1/asset/**").as("clearAssets"); + cy.get("#clear-filter").click(); + cy.wait("@clearAssets").its("response.statusCode").should("eq", 200); + cy.url().should("match", /\/assets$/); + } + clickadvancefilter() { + cy.intercept("GET", "**/api/v1/getallfacilities/**").as("advancefilter"); + cy.get("#advanced-filter").click(); + cy.wait("@advancefilter").its("response.statusCode").should("eq", 200); + } + clickslideoverbackbutton() { + cy.get("#close-slide-over").click(); + } + // Assertions + assertFacilityText(text) { + cy.get("[data-testid=Facility]").should("contain", text); + } + assertAssetTypeText(text) { + cy.get("[data-testid='Asset Type']").should("contain", text); + } + assertAssetClassText(text) { + cy.get("[data-testid='Asset Class']").should("contain", text); + } + assertStatusText(text) { + cy.get("[data-testid=Status]").should("contain", text); + } + assertLocationText(text) { + cy.get("[data-testid=Location]").should("contain", text); + } } diff --git a/cypress/pageobject/Asset/AssetSearch.ts b/cypress/pageobject/Asset/AssetSearch.ts index fea6a975983..b1ccb2f71c9 100644 --- a/cypress/pageobject/Asset/AssetSearch.ts +++ b/cypress/pageobject/Asset/AssetSearch.ts @@ -1,6 +1,6 @@ export class AssetSearchPage { typeSearchKeyword(keyword: string) { - cy.get("#search").clear(); + cy.get("#search").click().clear(); cy.get("#search").click().type(keyword); } @@ -9,7 +9,9 @@ export class AssetSearchPage { } clickAssetByName(assetName: string) { + cy.intercept("GET", "**/api/v1/asset/**").as("clearAssets"); cy.get("[data-testid='created-asset-list']").contains(assetName).click(); + cy.wait("@clearAssets").its("response.statusCode").should("eq", 200); } verifyBadgeContent(expectedText: string) { diff --git a/src/CAREUI/interactive/SlideOver.tsx b/src/CAREUI/interactive/SlideOver.tsx index 3eae13869cb..34e1c615f94 100644 --- a/src/CAREUI/interactive/SlideOver.tsx +++ b/src/CAREUI/interactive/SlideOver.tsx @@ -109,6 +109,7 @@ export default function SlideOver({ >