diff --git a/cypress/e2e/patient_spec/patient_prescription.cy.ts b/cypress/e2e/patient_spec/patient_prescription.cy.ts index 3cf5a1ba5aa..d9a7bf7a080 100644 --- a/cypress/e2e/patient_spec/patient_prescription.cy.ts +++ b/cypress/e2e/patient_spec/patient_prescription.cy.ts @@ -5,6 +5,11 @@ import { PatientPage } from "../../pageobject/Patient/PatientCreation"; const patientPrescription = new PatientPrescription(); const loginPage = new LoginPage(); const patientPage = new PatientPage(); +const medicineName = "DOLO"; +const medicineBaseDosage = "4"; +const medicineTargetDosage = "9"; +const medicineFrequency = "Twice daily"; +const medicineAdministerNote = "Medicine Administration Note"; describe("Patient Medicine Administration", () => { before(() => { @@ -18,15 +23,54 @@ describe("Patient Medicine Administration", () => { cy.awaitUrl("/patients"); }); + it("Add a new titrated medicine for a patient | Individual Administeration |", () => { + patientPage.visitPatient("Dummy Patient 5"); + patientPrescription.visitMedicineTab(); + patientPrescription.visitEditPrescription(); + patientPrescription.clickAddPrescription(); + patientPrescription.interceptMedibase(); + patientPrescription.selectMedicinebox(); + patientPrescription.selectMedicine(medicineName); + patientPrescription.clickTitratedDosage(); + patientPrescription.enterDosage(medicineBaseDosage); + patientPrescription.enterTargetDosage(medicineTargetDosage); + patientPrescription.selectDosageFrequency(medicineFrequency); + cy.submitButton("Submit"); + cy.verifyNotification("Medicine prescribed"); + cy.closeNotification(); + // Administer the medicine in edit form + patientPrescription.clickAdministerButton(); + patientPrescription.enterAdministerDosage(medicineBaseDosage); + patientPrescription.enterAdministerNotes(medicineAdministerNote); + cy.submitButton("Administer Medicine"); + cy.verifyNotification("Medicine(s) administered"); + cy.closeNotification(); + // Verify the Reflection on the Medicine + cy.verifyContentPresence("#medicine-preview", [ + medicineName, + medicineBaseDosage, + medicineTargetDosage, + ]); + patientPrescription.clickReturnToDashboard(); + // Go to medicine tab and administer it again + patientPrescription.visitMedicineTab(); + cy.verifyAndClickElement("#0", medicineName); + cy.submitButton("Administer"); + patientPrescription.enterAdministerDosage(medicineBaseDosage); + cy.submitButton("Administer Medicine"); + cy.verifyNotification("Medicine(s) administered"); + }); + it("Add a new medicine for a patient and verify the duplicate medicine validation", () => { patientPage.visitPatient("Dummy Patient 4"); patientPrescription.visitMedicineTab(); + patientPrescription.visitEditPrescription(); patientPrescription.clickAddPrescription(); patientPrescription.interceptMedibase(); patientPrescription.selectMedicinebox(); - patientPrescription.selectMedicine("DOLO"); - patientPrescription.enterDosage("4"); - patientPrescription.selectDosageFrequency("Twice daily"); + patientPrescription.selectMedicine(medicineName); + patientPrescription.enterDosage(medicineBaseDosage); + patientPrescription.selectDosageFrequency(medicineFrequency); cy.submitButton("Submit"); cy.verifyNotification("Medicine prescribed"); cy.closeNotification(); @@ -34,9 +78,9 @@ describe("Patient Medicine Administration", () => { patientPrescription.clickAddPrescription(); patientPrescription.interceptMedibase(); patientPrescription.selectMedicinebox(); - patientPrescription.selectMedicine("DOLO"); - patientPrescription.enterDosage("4"); - patientPrescription.selectDosageFrequency("Twice daily"); + patientPrescription.selectMedicine(medicineName); + patientPrescription.enterDosage(medicineBaseDosage); + patientPrescription.selectDosageFrequency(medicineFrequency); cy.submitButton("Submit"); cy.verifyNotification( "Medicine - This medicine is already prescribed to this patient. Please discontinue the existing prescription to prescribe again.", diff --git a/cypress/pageobject/Patient/PatientPrescription.ts b/cypress/pageobject/Patient/PatientPrescription.ts index dc4163e4823..a4b92b0a5fa 100644 --- a/cypress/pageobject/Patient/PatientPrescription.ts +++ b/cypress/pageobject/Patient/PatientPrescription.ts @@ -1,8 +1,10 @@ export class PatientPrescription { clickAddPrescription() { - cy.contains("button", "Add Prescription Medication") - .should("be.visible") - .click(); + cy.get("#add-prescription").scrollIntoView(); + cy.verifyAndClickElement( + "#add-prescription", + "Add Prescription Medication", + ); } interceptMedibase() { @@ -16,6 +18,15 @@ export class PatientPrescription { ); } + clickTitratedDosage() { + cy.get("#titrated-dosage").click(); + } + + clickAdministerButton() { + cy.get("#administer-medicine").should("be.visible"); + cy.verifyAndClickElement("#administer-medicine", "Administer"); + } + selectMedicinebox() { cy.get( "div#medicine_object input[placeholder='Select'][role='combobox']", @@ -30,6 +41,18 @@ export class PatientPrescription { cy.get("#base_dosage").type(doseAmount, { force: true }); } + enterAdministerDosage(dosage: string) { + cy.get("#dosage").type(dosage); + } + + enterAdministerNotes(notes: string) { + cy.get("#administration_notes").type(notes); + } + + enterTargetDosage(targetDosage: string) { + cy.get("#target_dosage").type(targetDosage, { force: true }); + } + selectDosageFrequency(frequency: string) { cy.clickAndSelectOption("#frequency", frequency); } @@ -54,7 +77,10 @@ export class PatientPrescription { visitMedicineTab() { cy.get("#consultation_tab_nav").scrollIntoView(); cy.get("#consultation_tab_nav").contains("Medicines").click(); - cy.get("a[href='prescriptions']").first().click(); + } + + visitEditPrescription() { + cy.get("#edit-prescription").click(); } } export default PatientPrescription; diff --git a/src/Common/hooks/useNotificationSubscriptionState.ts b/src/Common/hooks/useNotificationSubscriptionState.ts new file mode 100644 index 00000000000..b7927e0cc70 --- /dev/null +++ b/src/Common/hooks/useNotificationSubscriptionState.ts @@ -0,0 +1,55 @@ +import { useEffect, useState } from "react"; +import routes from "../../Redux/api"; +import request from "../../Utils/request/request"; +import useAuthUser from "./useAuthUser"; +import * as Sentry from "@sentry/browser"; + +export type NotificationSubscriptionState = + | "unsubscribed" + | "subscribed" + | "subscribed_on_other_device" + | "subscribed" + | "pending" + | "error"; + +/** + * This is a temporary hook and will be removed in the future. + * + * This hook is used to get the initial notification subscription state of the user. + * @returns NotificationSubscriptionState + */ +export default function useNotificationSubscriptionState( + dependencies: any[] = [], +) { + const { username } = useAuthUser(); + const [subscriptionState, setSubscriptionState] = + useState("pending"); + + const getSubscriptionState = async () => { + try { + const res = await request(routes.getUserPnconfig, { + pathParams: { username }, + }); + + const reg = await navigator.serviceWorker.ready; + const subscription = await reg.pushManager.getSubscription(); + + if (!subscription && !res.data?.pf_endpoint) { + setSubscriptionState("unsubscribed"); + } else if (subscription?.endpoint !== res.data?.pf_endpoint) { + setSubscriptionState("subscribed_on_other_device"); + } else { + setSubscriptionState("subscribed"); + } + } catch (error) { + setSubscriptionState("error"); + Sentry.captureException(error); + } + }; + + useEffect(() => { + getSubscriptionState(); + }, [username, ...dependencies]); + + return subscriptionState; +} diff --git a/src/Components/ABDM/ABDMFacilityRecords.tsx b/src/Components/ABDM/ABDMFacilityRecords.tsx index 8178e037a7f..96040905d6c 100644 --- a/src/Components/ABDM/ABDMFacilityRecords.tsx +++ b/src/Components/ABDM/ABDMFacilityRecords.tsx @@ -4,6 +4,8 @@ import useQuery from "../../Utils/request/useQuery"; import { formatDateTime } from "../../Utils/utils"; import Loading from "../Common/Loading"; import Page from "../Common/components/Page"; +import CareIcon from "../../CAREUI/icons/CareIcon"; +import ButtonV2 from "../Common/components/ButtonV2"; interface IProps { facilityId: string; @@ -21,7 +23,11 @@ const TableHeads = [ ]; export default function ABDMFacilityRecords({ facilityId }: IProps) { - const { data: consentsResult, loading } = useQuery(routes.abha.listConsents, { + const { + data: consentsResult, + loading, + refetch, + } = useQuery(routes.abha.listConsents, { query: { facility: facilityId, ordering: "-created_date" }, }); @@ -53,6 +59,13 @@ export default function ABDMFacilityRecords({ facilityId }: IProps) { scope="col" className="sticky right-0 top-0 py-3.5 pl-3 pr-4 sm:pr-6" > + refetch()} + ghost + className="max-w-2xl text-sm text-gray-700 hover:text-gray-900" + > + Refresh + View diff --git a/src/Components/ABDM/ABDMRecordsTab.tsx b/src/Components/ABDM/ABDMRecordsTab.tsx index 7fe38011796..9100f368216 100644 --- a/src/Components/ABDM/ABDMRecordsTab.tsx +++ b/src/Components/ABDM/ABDMRecordsTab.tsx @@ -170,6 +170,17 @@ export default function ABDMRecordsTab({ patientId }: IProps) { ; } + if (!data?.results.length) { + return ( +
+

No Records found

+

+ Raise a consent request to fetch patient records over ABDM +

+
+ ); + } + return (
{data?.results.map((record) => { diff --git a/src/Components/ABDM/FetchRecordsModal.tsx b/src/Components/ABDM/FetchRecordsModal.tsx index 8273a9763f1..950b69611c4 100644 --- a/src/Components/ABDM/FetchRecordsModal.tsx +++ b/src/Components/ABDM/FetchRecordsModal.tsx @@ -21,6 +21,7 @@ import CircularProgress from "../Common/components/CircularProgress.js"; import CareIcon from "../../CAREUI/icons/CareIcon.js"; import { classNames } from "../../Utils/utils.js"; import { ConsentHIType, ConsentPurpose } from "./types/consent.js"; +import useNotificationSubscriptionState from "../../Common/hooks/useNotificationSubscriptionState.js"; const getDate = (value: any) => value && dayjs(value).isValid() && dayjs(value).toDate(); @@ -46,6 +47,9 @@ export default function FetchRecordsModal({ patient, show, onClose }: IProps) { dayjs().add(30, "day").toDate(), ); const [errors, setErrors] = useState({}); + const notificationSubscriptionState = useNotificationSubscriptionState([ + show, + ]); useMessageListener((data) => { if (data.type === "MESSAGE" && data.from === "patients/on_find") { @@ -62,7 +66,23 @@ export default function FetchRecordsModal({ patient, show, onClose }: IProps) { }); return ( - + + {["unsubscribed", "subscribed_on_other_device"].includes( + notificationSubscriptionState, + ) && ( +

+ {" "} + Notifications needs to be enabled on this device to verify the + patient. +

+ )} +
Request Consent diff --git a/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx b/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx index 3176e18f2f3..543336987b2 100644 --- a/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx +++ b/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx @@ -43,7 +43,13 @@ const formatValue = (value: unknown, key?: string): ReactNode => { return `No ${key?.replaceAll(/_/g, " ")}`; } - return value.map((v) => formatValue(v, key)); + return ( +
    + {value.map((v) => ( +
  • {formatValue(v, key)}
  • + ))} +
+ ); } if (value instanceof Date) { @@ -77,7 +83,7 @@ export default function GenericEvent(props: IProps) { return (
{Object.entries(props.values).map(([key, value]) => ( -
+
{key.replaceAll(/_/g, " ")} diff --git a/src/Components/Facility/ConsultationDetails/index.tsx b/src/Components/Facility/ConsultationDetails/index.tsx index ffcd9166f23..4d393752888 100644 --- a/src/Components/Facility/ConsultationDetails/index.tsx +++ b/src/Components/Facility/ConsultationDetails/index.tsx @@ -431,6 +431,11 @@ export const ConsultationDetails = (props: any) => { ) return null; // Hide feed tab } + + if (p.text === "ABDM" && !patientData.abha_number) { + return null; + } + return ( { - try { - const res = await request(routes.getUserPnconfig, { - pathParams: { username }, + useEffect(() => { + if (notificationSubscriptionState === "unsubscribed") { + Notification.Warn({ + msg: "Please subscribe to notifications to get live updates on discussion notes.", + }); + } else if (notificationSubscriptionState === "subscribed_on_other_device") { + Notification.Warn({ + msg: "Please subscribe to notifications on this device to get live updates on discussion notes.", }); - const reg = await navigator.serviceWorker.ready; - const subscription = await reg.pushManager.getSubscription(); - if (!subscription && !res.data?.pf_endpoint) { - Notification.Warn({ - msg: "Please subscribe to notifications to get live updates on discussion notes.", - }); - } else if (subscription?.endpoint !== res.data?.pf_endpoint) { - Notification.Warn({ - msg: "Please subscribe to notifications on this device to get live updates on discussion notes.", - }); - } - } catch (error) { - Sentry.captureException(error); } - }; - - useEffect(() => { - intialSubscriptionState(); - }, []); + }, [notificationSubscriptionState]); const initialData: PatientNoteStateType = { notes: [], diff --git a/src/Components/Medicine/CreatePrescriptionForm.tsx b/src/Components/Medicine/CreatePrescriptionForm.tsx index b918d5781fe..173f5163427 100644 --- a/src/Components/Medicine/CreatePrescriptionForm.tsx +++ b/src/Components/Medicine/CreatePrescriptionForm.tsx @@ -64,6 +64,7 @@ export default function CreatePrescriptionForm(props: { {props.prescription.dosage_type !== "PRN" && props.prescription.prescription_type !== "DISCHARGE" && ( -
+

diff --git a/src/Components/Medicine/MedicineAdministrationSheet/AdministrationEventCell.tsx b/src/Components/Medicine/MedicineAdministrationSheet/AdministrationEventCell.tsx index b13e05b5b58..0949189d96d 100644 --- a/src/Components/Medicine/MedicineAdministrationSheet/AdministrationEventCell.tsx +++ b/src/Components/Medicine/MedicineAdministrationSheet/AdministrationEventCell.tsx @@ -62,6 +62,7 @@ export default function AdministrationEventCell({ />