Skip to content

Commit

Permalink
Merge branch 'develop' into issues/7810/LinkedSkills-for-treating-doctor
Browse files Browse the repository at this point in the history
  • Loading branch information
nihal467 authored May 21, 2024
2 parents 760f6f0 + aa7d7aa commit 3f18337
Show file tree
Hide file tree
Showing 15 changed files with 221 additions and 42 deletions.
56 changes: 50 additions & 6 deletions cypress/e2e/patient_spec/patient_prescription.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => {
Expand All @@ -18,25 +23,64 @@ 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();
// verify the duplicate medicine error message
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.",
Expand Down
34 changes: 30 additions & 4 deletions cypress/pageobject/Patient/PatientPrescription.ts
Original file line number Diff line number Diff line change
@@ -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() {
Expand All @@ -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']",
Expand All @@ -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);
}
Expand All @@ -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;
55 changes: 55 additions & 0 deletions src/Common/hooks/useNotificationSubscriptionState.ts
Original file line number Diff line number Diff line change
@@ -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<NotificationSubscriptionState>("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;
}
15 changes: 14 additions & 1 deletion src/Components/ABDM/ABDMFacilityRecords.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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" },
});

Expand Down Expand Up @@ -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"
>
<ButtonV2
onClick={() => refetch()}
ghost
className="max-w-2xl text-sm text-gray-700 hover:text-gray-900"
>
<CareIcon icon="l-refresh" /> Refresh
</ButtonV2>
<span className="sr-only">View</span>
</th>
</tr>
Expand Down
11 changes: 11 additions & 0 deletions src/Components/ABDM/ABDMRecordsTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,17 @@ export default function ABDMRecordsTab({ patientId }: IProps) {
<Loading />;
}

if (!data?.results.length) {
return (
<div className="mt-12 flex flex-col items-center justify-center gap-2.5">
<p className="font-semibold text-gray-600">No Records found</p>
<p className="text-sm text-gray-600">
Raise a consent request to fetch patient records over ABDM
</p>
</div>
);
}

return (
<div className="mt-6 flex flex-col gap-6">
{data?.results.map((record) => {
Expand Down
30 changes: 28 additions & 2 deletions src/Components/ABDM/FetchRecordsModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -46,6 +47,9 @@ export default function FetchRecordsModal({ patient, show, onClose }: IProps) {
dayjs().add(30, "day").toDate(),
);
const [errors, setErrors] = useState<any>({});
const notificationSubscriptionState = useNotificationSubscriptionState([
show,
]);

useMessageListener((data) => {
if (data.type === "MESSAGE" && data.from === "patients/on_find") {
Expand All @@ -62,7 +66,23 @@ export default function FetchRecordsModal({ patient, show, onClose }: IProps) {
});

return (
<DialogModal title="Fetch Records over ABDM" show={show} onClose={onClose}>
<DialogModal
className="max-w-xl"
fixedWidth={false}
title="Fetch Records over ABDM"
show={show}
onClose={onClose}
>
{["unsubscribed", "subscribed_on_other_device"].includes(
notificationSubscriptionState,
) && (
<p className="my-4 text-sm text-warning-600">
<CareIcon icon="l-exclamation-triangle" className="h-4 w-4" />{" "}
Notifications needs to be enabled on this device to verify the
patient.
</p>
)}

<div className="flex items-center gap-3">
<TextFormField
value={patient?.abha_number_object?.health_id as string}
Expand All @@ -89,7 +109,12 @@ export default function FetchRecordsModal({ patient, show, onClose }: IProps) {
}}
loading={idVerificationStatus === "in-progress"}
ghost={idVerificationStatus === "verified"}
disabled={idVerificationStatus === "verified"}
disabled={
idVerificationStatus === "verified" ||
["unsubscribed", "subscribed_on_other_device"].includes(
notificationSubscriptionState,
)
}
className={classNames(
"mt-1.5 !py-3",
idVerificationStatus === "verified" &&
Expand Down Expand Up @@ -215,6 +240,7 @@ export default function FetchRecordsModal({ patient, show, onClose }: IProps) {
setIsMakingConsentRequest(false);
onClose();
}}
disabled={idVerificationStatus !== "verified"}
loading={isMakingConsentRequest}
>
Request Consent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ const formatValue = (value: unknown, key?: string): ReactNode => {
return `No ${key?.replaceAll(/_/g, " ")}`;
}

return value.map((v) => formatValue(v, key));
return (
<ul className="list-disc space-y-2 pl-4">
{value.map((v) => (
<li>{formatValue(v, key)}</li>
))}
</ul>
);
}

if (value instanceof Date) {
Expand Down Expand Up @@ -77,7 +83,7 @@ export default function GenericEvent(props: IProps) {
return (
<div className="flex w-full flex-col gap-4 rounded-lg border border-gray-400 p-4 @container">
{Object.entries(props.values).map(([key, value]) => (
<div className="flex w-full flex-col items-center gap-2 md:flex-row">
<div className="flex w-full flex-col items-start gap-2">
<span className="text-xs uppercase text-gray-700">
{key.replaceAll(/_/g, " ")}
</span>
Expand Down
5 changes: 5 additions & 0 deletions src/Components/Facility/ConsultationDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,11 @@ export const ConsultationDetails = (props: any) => {
)
return null; // Hide feed tab
}

if (p.text === "ABDM" && !patientData.abha_number) {
return null;
}

return (
<Link
key={p.text}
Expand Down
Loading

0 comments on commit 3f18337

Please sign in to comment.