Skip to content

Commit

Permalink
Redesign Doctor Notes (Review, QA, Testing) (#6224)
Browse files Browse the repository at this point in the history
* feat: add patient notes pop-up on consultation page

* Migrate form elements to CAREUI

* made patient notes popup responsive

* feat: updated ui of patient notes dedicated page

* resolved merge conflicts

* fix merge develop

* added doctor location

* used Page component in PatientNotes page

* Apply suggestions from code review

* added close button

* Apply suggestions from code review

* enchance doctor notes ux

* Merge branch 'develop' into doctor-notes-redesign

* replace usages of moment with dayjs

* Added user role in brackets

* added message listener for patient notes

* added border for notes by remote specialist

* added notification on patient note creation

* Update src/Components/Facility/PatientNoteCard.tsx

Co-authored-by: Rithvik Nishad <[email protected]>

* added types for notes object

* use existing types of patient notes model

* link system notification to notes page

* Fixes for real time webpush messages

* Fixes for notes popover

* Update service-worker.ts

* lint

---------

Co-authored-by: Rithvik Nishad <[email protected]>
Co-authored-by: Rithvik Nishad <[email protected]>
Co-authored-by: Ashesh3 <[email protected]>
  • Loading branch information
4 people authored Nov 23, 2023
1 parent 39cd0fa commit f804e6d
Show file tree
Hide file tree
Showing 14 changed files with 505 additions and 177 deletions.
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"react-dom": "18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-i18next": "^13.0.1",
"react-infinite-scroll-component": "^6.1.0",
"react-markdown": "^8.0.7",
"react-player": "^2.13.0",
"react-qr-reader": "^2.2.1",
Expand Down
12 changes: 3 additions & 9 deletions src/CAREUI/icons/UniconPaths.json
Original file line number Diff line number Diff line change
Expand Up @@ -3107,10 +3107,7 @@
24,
"M19,2H5A3,3,0,0,0,2,5V19a3,3,0,0,0,3,3H19a3,3,0,0,0,3-3V5A3,3,0,0,0,19,2Zm1,17a1,1,0,0,1-1,1H5a1,1,0,0,1-1-1V5A1,1,0,0,1,5,4H19a1,1,0,0,1,1,1Zm-4-8H8a1,1,0,0,0,0,2h8a1,1,0,0,0,0-2Z"
],
"l-minus": [
24,
"M19,11H5a1,1,0,0,0,0,2H19a1,1,0,0,0,0-2Z"
],
"l-minus": [24, "M19,11H5a1,1,0,0,0,0,2H19a1,1,0,0,0,0-2Z"],
"l-missed-call": [
24,
"M6,7.49a1,1,0,0,0,1-1V5.9L9.88,8.78a3,3,0,0,0,4.24,0l4.59-4.59a1,1,0,0,0,0-1.41,1,1,0,0,0-1.42,0L12.71,7.36a1,1,0,0,1-1.42,0L8.41,4.49H9a1,1,0,0,0,0-2H6a1,1,0,0,0-.92.61A1.09,1.09,0,0,0,5,3.49v3A1,1,0,0,0,6,7.49Zm15.94,7.36a16.27,16.27,0,0,0-19.88,0,2.69,2.69,0,0,0-1,2,2.66,2.66,0,0,0,.78,2.07L3.6,20.72A2.68,2.68,0,0,0,7.06,21l.47-.32a8.13,8.13,0,0,1,1-.55,1.85,1.85,0,0,0,1-2.3l-.09-.24a10.49,10.49,0,0,1,5.22,0l-.09.24a1.85,1.85,0,0,0,1,2.3,8.13,8.13,0,0,1,1,.55l.47.32a2.58,2.58,0,0,0,1.54.5,2.72,2.72,0,0,0,1.92-.79l1.81-1.82A2.66,2.66,0,0,0,23,16.83,2.69,2.69,0,0,0,21.94,14.85ZM20.8,17.49,19,19.3a.68.68,0,0,1-.86.1c-.19-.14-.38-.27-.59-.4a11.65,11.65,0,0,0-1.09-.61l.4-1.09a1,1,0,0,0-.6-1.28,12.42,12.42,0,0,0-8.5,0,1,1,0,0,0-.6,1.28l.4,1.1a9.8,9.8,0,0,0-1.1.6l-.58.4A.66.66,0,0,1,5,19.3L3.2,17.49A.67.67,0,0,1,3,17a.76.76,0,0,1,.28-.53,14.29,14.29,0,0,1,17.44,0A.76.76,0,0,1,21,17,.67.67,0,0,1,20.8,17.49Z"
Expand Down Expand Up @@ -4295,10 +4292,7 @@
24,
"M15,13H9a1,1,0,0,0,0,2h2v2a1,1,0,0,0,2,0V15h2a1,1,0,0,0,0-2Zm2-7H7A1,1,0,0,0,7,8h4v2a1,1,0,0,0,2,0V8h4a1,1,0,0,0,0-2Z"
],
"l-text": [
24,
"M17,6H7A1,1,0,0,0,7,8h4v9a1,1,0,0,0,2,0V8h4a1,1,0,0,0,0-2Z"
],
"l-text": [24, "M17,6H7A1,1,0,0,0,7,8h4v9a1,1,0,0,0,2,0V8h4a1,1,0,0,0,0-2Z"],
"l-th-large": [
24,
"M20,3H4A1,1,0,0,0,3,4V20a1,1,0,0,0,1,1H20a1,1,0,0,0,1-1V4A1,1,0,0,0,20,3ZM11,19H5V13h6Zm0-8H5V5h6Zm8,8H13V13h6Zm0-8H13V5h6Z"
Expand Down Expand Up @@ -4841,4 +4835,4 @@
256,
"M204.73 51.85A108.07 108.07 0 0 0 20 128v56a28 28 0 0 0 28 28h16a28 28 0 0 0 28-28v-40a28 28 0 0 0-28-28H44.84A84.05 84.05 0 0 1 128 44h.64a83.7 83.7 0 0 1 82.52 72H192a28 28 0 0 0-28 28v40a28 28 0 0 0 28 28h19.6a20 20 0 0 1-19.6 16h-56a12 12 0 0 0 0 24h56a44.05 44.05 0 0 0 44-44v-80a107.34 107.34 0 0 0-31.27-76.15ZM64 140a4 4 0 0 1 4 4v40a4 4 0 0 1-4 4H48a4 4 0 0 1-4-4v-44Zm124 44v-40a4 4 0 0 1 4-4h20v48h-20a4 4 0 0 1-4-4Z"
]
}
}
22 changes: 22 additions & 0 deletions src/Common/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,11 @@ export const NOTIFICATION_EVENTS = [
text: "Shifting Updated",
icon: "fa-solid fa-truck-medical",
},
{
id: "PATIENT_NOTE_ADDED",
text: "Patient Note Added",
icon: "fa-solid fa-message",
},
];

export const BREATHLESSNESS_LEVEL = [
Expand Down Expand Up @@ -1015,6 +1020,23 @@ export const XLSXAssetImportSchema = {
},
};

export const USER_TYPES_MAP = {
Pharmacist: "Pharmacist",
Volunteer: "Volunteer",
StaffReadOnly: "Staff",
Staff: "Staff",
Doctor: "Doctor",
WardAdmin: "Ward Admin",
LocalBodyAdmin: "Local Body Admin",
DistrictLabAdmin: "District Lab Admin",
DistrictReadOnlyAdmin: "District Admin",
DistrictAdmin: "District Admin",
StateLabAdmin: "State Lab Admin",
StateReadOnlyAdmin: "State Admin",
StateAdmin: "State Admin",
RemoteSpecialist: "Remote Specialist",
};

export const AREACODES: Record<string, string[]> = {
CA: [
"403",
Expand Down
15 changes: 12 additions & 3 deletions src/Components/Facility/ConsultationDetails/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,8 @@ import PatientInfoCard from "../../Patient/PatientInfoCard";
import { PatientModel } from "../../Patient/models";
import { formatDateTime, relativeTime } from "../../../Utils/utils";

import { navigate } from "raviger";
import { navigate, useQueryParams } from "raviger";
import { useDispatch } from "react-redux";
import { useQueryParams } from "raviger";
import { useTranslation } from "react-i18next";
import { triggerGoal } from "../../../Integrations/Plausible";
import useAuthUser from "../../../Common/hooks/useAuthUser";
Expand All @@ -42,6 +41,7 @@ import { ConsultationPressureSoreTab } from "./ConsultationPressureSoreTab";
import { ConsultationDialysisTab } from "./ConsultationDialysisTab";
import { ConsultationNeurologicalMonitoringTab } from "./ConsultationNeurologicalMonitoringTab";
import { ConsultationNutritionTab } from "./ConsultationNutritionTab";
import PatientNotesSlideover from "../PatientNotesSlideover";
import LegacyDiagnosesList from "../../Diagnosis/LegacyDiagnosesList";

const Loading = lazy(() => import("../../Common/Loading"));
Expand Down Expand Up @@ -105,6 +105,7 @@ export const ConsultationDetails = (props: any) => {
return "None";
}
};
const [showPatientNotesPopup, setShowPatientNotesPopup] = useState(false);

const authUser = useAuthUser();

Expand Down Expand Up @@ -370,7 +371,7 @@ export const ConsultationDetails = (props: any) => {
</Link>
<Link
id="patient_doctor_notes"
href={`/facility/${patientData.facility}/patient/${patientData.id}/notes`}
onClick={() => setShowPatientNotesPopup(true)}
className="btn btn-primary m-1 w-full hover:text-white"
>
Doctor&apos;s Notes
Expand Down Expand Up @@ -540,6 +541,14 @@ export const ConsultationDetails = (props: any) => {
show={showDoctors}
setShow={setShowDoctors}
/>

{showPatientNotesPopup && (
<PatientNotesSlideover
patientId={patientId}
facilityId={facilityId}
setShowPatientNotesPopup={setShowPatientNotesPopup}
/>
)}
</div>
);
};
37 changes: 37 additions & 0 deletions src/Components/Facility/PatientNoteCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { relativeDate, formatDateTime, classNames } from "../../Utils/utils";
import { USER_TYPES_MAP } from "../../Common/constants";
import { PatientNotesModel } from "./models";

const PatientNoteCard = ({ note }: { note: PatientNotesModel }) => {
return (
<div
className={classNames(
"mt-4 flex w-full flex-col rounded-lg border border-gray-300 bg-white p-3 text-gray-800",
note.user_type === "RemoteSpecialist" && "border-primary-400"
)}
>
<div className="flex">
<span className="text-sm font-semibold text-gray-700">
{note.created_by_object?.first_name || "Unknown"}{" "}
{note.created_by_object?.last_name}
</span>
{note.user_type && (
<span className="pl-2 text-sm text-gray-700">
{`(${USER_TYPES_MAP[note.user_type]})`}
</span>
)}
</div>
<span className="whitespace-pre-wrap break-words">{note.note}</span>
<div className="mt-3 text-end text-xs text-gray-500">
<div className="tooltip inline">
<span className="tooltip-text tooltip-left">
{formatDateTime(note.created_date)}
</span>
{relativeDate(note.created_date)}
</div>
</div>
</div>
);
};

export default PatientNoteCard;
127 changes: 127 additions & 0 deletions src/Components/Facility/PatientNotesList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { useCallback, useState, useEffect } from "react";
import { useDispatch } from "react-redux";
import { statusType, useAbortableEffect } from "../../Common/utils";
import { getPatientNotes } from "../../Redux/actions";
import { RESULTS_PER_PAGE_LIMIT } from "../../Common/constants";
import CircularProgress from "../Common/components/CircularProgress";
import PatientNoteCard from "./PatientNoteCard";
import InfiniteScroll from "react-infinite-scroll-component";
import { NoteType } from "./PatientNoteCard";

interface PatientNotesProps {
patientId: any;
facilityId: any;
reload?: boolean;
setReload?: any;
}

interface StateType {
notes: NoteType[];
cPage: number;
totalPages: number;
}

const pageSize = RESULTS_PER_PAGE_LIMIT;

const PatientNotesList = (props: PatientNotesProps) => {
const { reload, setReload } = props;

const dispatch: any = useDispatch();
const initialData: StateType = { notes: [], cPage: 1, totalPages: 1 };
const [state, setState] = useState(initialData);
const [isLoading, setIsLoading] = useState(true);

const fetchData = useCallback(
async (page = 1, status: statusType = { aborted: false }) => {
setIsLoading(true);
const res = await dispatch(
getPatientNotes(props.patientId, pageSize, (page - 1) * pageSize)
);
if (!status.aborted) {
if (res && res.data) {
if (page === 1) {
setState({
notes: res.data?.results,
cPage: page,
totalPages: Math.ceil(res.data.count / pageSize),
});
} else {
setState((prevState: any) => ({
...prevState,
notes: [...prevState.notes, ...res.data.results],
cPage: page,
totalPages: Math.ceil(res.data.count / pageSize),
}));
}
}
setIsLoading(false);
}
},
[props.patientId, dispatch]
);

useEffect(() => {
if (reload) {
fetchData(1);
setReload(false);
}
}, [reload]);

useAbortableEffect(
(status: statusType) => {
fetchData(1, status);
},
[fetchData]
);

const handleNext = () => {
if (state.cPage < state.totalPages) {
fetchData(state.cPage + 1);
setState((prevState: any) => ({
...prevState,
cPage: prevState.cPage + 1,
}));
}
};

if (isLoading && !state.notes.length) {
return (
<div className=" flex h-[400px] w-full items-center justify-center bg-white">
<CircularProgress />
</div>
);
}

return (
<div
className="m-2 flex h-[390px] grow flex-col-reverse overflow-auto bg-white"
id="patient-notes-list"
>
{state.notes.length ? (
<InfiniteScroll
next={handleNext}
hasMore={state.cPage < state.totalPages}
loader={
<div className="flex items-center justify-center">
<CircularProgress />
</div>
}
className="flex h-full flex-col-reverse p-2"
inverse={true}
dataLength={state.notes.length}
scrollableTarget="patient-notes-list"
>
{state.notes.map((note: any) => (
<PatientNoteCard note={note} key={note.id} />
))}
</InfiniteScroll>
) : (
<div className="mt-2 flex items-center justify-center text-2xl font-bold text-gray-500">
No Notes Found
</div>
)}
</div>
);
};

export default PatientNotesList;
Loading

0 comments on commit f804e6d

Please sign in to comment.