From 68b0230859f366ef486102e1a54e912526e93bce Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Wed, 17 Jul 2024 16:20:26 +0530 Subject: [PATCH 1/3] Add popup to acknowledge discharge --- src/Common/hooks/useConfirmedAction.ts | 17 + src/Components/Facility/DischargeModal.tsx | 438 +++++++++++---------- src/Locale/en/Common.json | 3 +- 3 files changed, 253 insertions(+), 205 deletions(-) create mode 100644 src/Common/hooks/useConfirmedAction.ts diff --git a/src/Common/hooks/useConfirmedAction.ts b/src/Common/hooks/useConfirmedAction.ts new file mode 100644 index 00000000000..ca88a5014ed --- /dev/null +++ b/src/Common/hooks/useConfirmedAction.ts @@ -0,0 +1,17 @@ +import { useState } from "react"; + +export default function useConfirmedAction(action: () => Promise) { + const [showConfirmation, setShowConfirmation] = useState(false); + + return { + requestConfirmation: () => setShowConfirmation(true), + submit: action, + + confirmationProps: { + onClose: () => setShowConfirmation(false), + show: showConfirmation, + onConfirm: action, + action: "Submit", + }, + }; +} diff --git a/src/Components/Facility/DischargeModal.tsx b/src/Components/Facility/DischargeModal.tsx index a85439bfa55..a090b359e93 100644 --- a/src/Components/Facility/DischargeModal.tsx +++ b/src/Components/Facility/DischargeModal.tsx @@ -26,6 +26,8 @@ import { FacilityModel } from "./models"; import dayjs from "../../Utils/dayjs"; import { FieldError } from "../Form/FieldValidators"; import { useTranslation } from "react-i18next"; +import useConfirmedAction from "../../Common/hooks/useConfirmedAction"; +import ConfirmDialog from "../Common/ConfirmDialog"; interface PreDischargeFormInterface { new_discharge_reason: number | null; @@ -60,6 +62,7 @@ const DischargeModal = ({ }: IProps) => { const { t } = useTranslation(); const { enable_hcx } = useConfig(); + const dispatch: any = useDispatch(); const [preDischargeForm, setPreDischargeForm] = useState({ @@ -130,14 +133,12 @@ const DischargeModal = ({ } }); - const handlePatientDischarge = async (value: boolean) => { - setIsSendingDischargeApi(true); + const validate = () => { if (!new_discharge_reason && !discharge_reason) { setErrors({ ...errors, new_discharge_reason: "Please select a reason for discharge", }); - setIsSendingDischargeApi(false); return; } @@ -155,45 +156,32 @@ const DischargeModal = ({ if (Object.entries(newErrors).length) { setErrors({ ...errors, ...newErrors }); - setIsSendingDischargeApi(false); return; } } - const dischargeDetails = { - ...preDischargeForm, - discharge: value, - referred_to: referred_to?.id ?? preDischargeForm.referred_to, - discharge_date: dayjs(preDischargeForm.discharge_date).toISOString(), - }; - - if (dischargeDetails.referred_to != undefined) - delete dischargeDetails.referred_to_external; - - if (dischargeDetails.referred_to_external != undefined) - delete dischargeDetails.referred_to; + return true; + }; + const submitAction = useConfirmedAction(async () => { + setIsSendingDischargeApi(true); const dischargeResponse = await dispatch( dischargePatient( { ...preDischargeForm, - discharge: value, new_discharge_reason: discharge_reason, discharge_date: dayjs(preDischargeForm.discharge_date).toISOString(), }, { id: consultationData.id }, ), ); - setIsSendingDischargeApi(false); - if (dischargeResponse?.status === 200) { - Notification.Success({ - msg: "Patient Discharged Successfully", - }); + if (dischargeResponse?.status === 200) { + Notification.Success({ msg: "Patient Discharged Successfully" }); afterSubmit?.(); } - }; + }); const handleFacilitySelect = (selected?: FacilityModel) => { setFacility(selected ?? null); @@ -204,206 +192,248 @@ const DischargeModal = ({ })); }; - const encounterDuration = dayjs - .duration( - dayjs( - preDischargeForm[ - discharge_reason === - DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id - ? "death_datetime" - : "discharge_date" - ], - ).diff(consultationData.encounter_date), - ) - .humanize(); + const encounterDuration = dayjs.duration( + dayjs( + preDischargeForm[ + discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id + ? "death_datetime" + : "discharge_date" + ], + ).diff(consultationData.encounter_date), + ); + + const confirmationRequired = encounterDuration.asDays() >= 30; return ( - -

Discharge patient from CARE

- - -

Caution: this action is irreversible.

-
+ <> + +
+

+ Are you sure you want to close this encounter, noting that the + patient has been admitted for{" "} + + {Math.ceil(encounterDuration.asDays())} days + + {" ?"} +

+

+ By confirming, you acknowledge that no further edits can be made to + this encounter and that the information entered is accurate to the + best of your knowledge. +

- } - show={show} - onClose={onClose} - className="md:max-w-3xl" - > -
- id} - optionLabel={({ text }) => text} - onChange={(e) => - setPreDischargeForm((prev) => ({ - ...prev, - new_discharge_reason: e.value, - })) - } - error={errors?.new_discharge_reason} - /> - {discharge_reason === - DISCHARGE_REASONS.find((i) => i.text == "Referred")?.id && ( -
- Referred to - - handleFacilitySelect(selected as FacilityModel | undefined) - } - disabled={!!referred_to} - selected={facility ?? null} - showAll - freeText - multiple={false} - errors={errors?.referred_to} - className="mb-4" - /> + + +

Discharge patient from CARE

+ + +

+ {t("caution")}: {t("action_irreversible")} +

+
- )} - i.text == "Expired")?.id - } - label={ - { - "3": "Cause of death", - "1": "Discharged Advice", - }[discharge_reason ?? 0] ?? "Notes" - } - name="discharge_notes" - value={preDischargeForm.discharge_notes} - onChange={(e) => - setPreDischargeForm((prev) => ({ - ...prev, - discharge_notes: e.value, - })) + } + show={show} + onClose={() => { + if (!submitAction.confirmationProps.show) { + onClose(); } - error={errors?.discharge_notes} - /> - i.text == "Expired")?.id - ? "death_datetime" - : "discharge_date" - } - label={ - discharge_reason === - DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id - ? "Date of Death" - : "Date and Time of Discharge" - } - type="datetime-local" - value={ - preDischargeForm[ + }} + className="md:max-w-3xl" + > +
+ id} + optionLabel={({ text }) => text} + onChange={(e) => + setPreDischargeForm((prev) => ({ + ...prev, + new_discharge_reason: e.value, + })) + } + error={errors?.new_discharge_reason} + /> + {discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Referred")?.id && ( +
+ Referred to + + handleFacilitySelect(selected as FacilityModel | undefined) + } + disabled={!!referred_to} + selected={facility ?? null} + showAll + freeText + multiple={false} + errors={errors?.referred_to} + className="mb-4" + /> +
+ )} + i.text == "Expired")?.id + } + label={ + { + "3": "Cause of death", + "1": "Discharged Advice", + }[discharge_reason ?? 0] ?? "Notes" + } + name="discharge_notes" + value={preDischargeForm.discharge_notes} + onChange={(e) => + setPreDischargeForm((prev) => ({ + ...prev, + discharge_notes: e.value, + })) + } + error={errors?.discharge_notes} + /> + i.text == "Expired")?.id ? "death_datetime" : "discharge_date" - ] - } - onChange={(e) => { - const updates: Record = { - discharge_date: undefined, - death_datetime: undefined, - }; - updates[e.name] = e.value; - setPreDischargeForm((form) => ({ ...form, ...updates })); - }} - required - min={dayjs(consultationData?.encounter_date).format( - "YYYY-MM-DDTHH:mm", - )} - max={dayjs().format("YYYY-MM-DDTHH:mm")} - error={ - discharge_reason === - DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id - ? errors?.death_datetime - : errors?.discharge_date - } - /> - {discharge_reason === - DISCHARGE_REASONS.find((i) => i.text == "Recovered")?.id && ( - <> -
- Discharge Prescription Medications - -
-
- Discharge PRN Prescriptions - -
- - )} - {discharge_reason === - DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id && ( - i.text == "Expired")?.id + ? "Date of Death" + : "Date and Time of Discharge" + } + type="datetime-local" + value={ + preDischargeForm[ + discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id + ? "death_datetime" + : "discharge_date" + ] + } onChange={(e) => { - setPreDischargeForm((form) => { - return { - ...form, - death_confirmed_doctor: e.value, - }; - }); + const updates: Record = { + discharge_date: undefined, + death_datetime: undefined, + }; + updates[e.name] = e.value; + setPreDischargeForm((form) => ({ ...form, ...updates })); }} required - placeholder="Attending Doctor's Name and Designation" + min={dayjs(consultationData?.encounter_date).format( + "YYYY-MM-DDTHH:mm", + )} + max={dayjs().format("YYYY-MM-DDTHH:mm")} + error={ + discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id + ? errors?.death_datetime + : errors?.discharge_date + } /> + {discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Recovered")?.id && ( + <> +
+ Discharge Prescription Medications + +
+
+ Discharge PRN Prescriptions + +
+ + )} + {discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id && ( + { + setPreDischargeForm((form) => { + return { + ...form, + death_confirmed_doctor: e.value, + }; + }); + }} + required + placeholder="Attending Doctor's Name and Designation" + /> + )} +
+ + {enable_hcx && ( + // TODO: if policy and approved pre-auth exists +
+

Claim Insurance

+ {latestClaim ? ( + + ) : ( + + )} +
)} -
- {enable_hcx && ( - // TODO: if policy and approved pre-auth exists -
-

Claim Insurance

- {latestClaim ? ( - +
+ + {t("encounter_duration_confirmation")}{" "} + {encounterDuration.humanize()}. + +
+
+ + {isSendingDischargeApi ? ( + ) : ( - { + if (!validate()) { + return; + } + + if (confirmationRequired) { + submitAction.requestConfirmation(); + return; + } + + submitAction.submit(); + }} + label="Confirm Discharge" + autoFocus /> )}
- )} - -
- - {t("encounter_duration_confirmation")}{" "} - {encounterDuration}. - -
-
- - {isSendingDischargeApi ? ( - - ) : ( - handlePatientDischarge(false)} - label="Confirm Discharge" - autoFocus - /> - )} -
- + + ); }; diff --git a/src/Locale/en/Common.json b/src/Locale/en/Common.json index a85572b8d04..64b963c8b25 100644 --- a/src/Locale/en/Common.json +++ b/src/Locale/en/Common.json @@ -172,5 +172,6 @@ "ration_card__NO_CARD": "Non-card holder", "ration_card__BPL": "BPL", "ration_card__APL": "APL", - "caution": "Caution" + "caution": "Caution", + "action_irreversible": "This action is irreversible" } \ No newline at end of file From a678f9dbe99543483e392f1dc3624bc68252ff50 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Wed, 24 Jul 2024 10:00:22 +0530 Subject: [PATCH 2/3] modify cypress tests --- cypress/e2e/patient_spec/patient_discharge.cy.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cypress/e2e/patient_spec/patient_discharge.cy.ts b/cypress/e2e/patient_spec/patient_discharge.cy.ts index 34ad423d1e8..31685ff2732 100644 --- a/cypress/e2e/patient_spec/patient_discharge.cy.ts +++ b/cypress/e2e/patient_spec/patient_discharge.cy.ts @@ -36,6 +36,7 @@ describe("Patient Discharge based on multiple reason", () => { patientDischarge.clickDischarge(); patientDischarge.selectDischargeReason(patientDischargeReason4); cy.submitButton("Confirm Discharge"); + cy.submitButton("Acknowledge & Submit"); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); // Verify the consultation dashboard reflection @@ -53,6 +54,7 @@ describe("Patient Discharge based on multiple reason", () => { patientDischarge.typeDischargeNote(patientDeathCause); patientDischarge.typeDoctorName(doctorName); cy.submitButton("Confirm Discharge"); + cy.submitButton("Acknowledge & Submit"); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); // Verify the consultation dashboard reflection @@ -77,6 +79,10 @@ describe("Patient Discharge based on multiple reason", () => { patientDischarge.typeReferringFacility(referringFreetextFacility); cy.wait(2000); cy.submitButton("Confirm Discharge"); + cy.wait(500); + cy.submitButton("Cancel"); + cy.submitButton("Confirm Discharge"); + cy.submitButton("Acknowledge & Submit"); cy.wait(2000); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); @@ -108,6 +114,7 @@ describe("Patient Discharge based on multiple reason", () => { cy.closeNotification(); // submit the discharge pop-up cy.submitButton("Confirm Discharge"); + cy.submitButton("Acknowledge & Submit"); cy.wait(2000); cy.verifyNotification("Patient Discharged Successfully"); cy.closeNotification(); From 6a1a5624b3e220b532549d9dd144c755edf4bdf8 Mon Sep 17 00:00:00 2001 From: rithviknishad Date: Wed, 24 Jul 2024 10:24:03 +0530 Subject: [PATCH 3/3] fix cypress --- cypress/e2e/patient_spec/patient_discharge.cy.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/cypress/e2e/patient_spec/patient_discharge.cy.ts b/cypress/e2e/patient_spec/patient_discharge.cy.ts index 31685ff2732..242c936730d 100644 --- a/cypress/e2e/patient_spec/patient_discharge.cy.ts +++ b/cypress/e2e/patient_spec/patient_discharge.cy.ts @@ -79,9 +79,6 @@ describe("Patient Discharge based on multiple reason", () => { patientDischarge.typeReferringFacility(referringFreetextFacility); cy.wait(2000); cy.submitButton("Confirm Discharge"); - cy.wait(500); - cy.submitButton("Cancel"); - cy.submitButton("Confirm Discharge"); cy.submitButton("Acknowledge & Submit"); cy.wait(2000); cy.verifyNotification("Patient Discharged Successfully");