Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add edit option for doctor notes #6919

Merged
merged 9 commits into from
Feb 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/Components/Facility/ConsultationDoctorNotes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ const ConsultationDoctorNotes = (props: ConsultationDoctorNotesProps) => {
notes: [],
cPage: 1,
totalPages: 1,
facilityId: facilityId,
patientId: patientId,
};
const [state, setState] = useState(initialData);

Expand Down Expand Up @@ -113,8 +115,6 @@ const ConsultationDoctorNotes = (props: ConsultationDoctorNotesProps) => {
<PatientConsultationNotesList
state={state}
setState={setState}
patientId={patientId}
facilityId={facilityId}
reload={reload}
setReload={setReload}
/>
Expand Down
12 changes: 10 additions & 2 deletions src/Components/Facility/DoctorNote.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import { PatientNoteStateType } from "./models";

interface DoctorNoteProps {
state: PatientNoteStateType;
setReload: any;
handleNext: () => void;
disableEdit?: boolean;
}

const DoctorNote = (props: DoctorNoteProps) => {
const { state, handleNext } = props;
const { state, handleNext, setReload, disableEdit } = props;
return (
<div
className="m-2 flex h-[390px] grow flex-col-reverse overflow-auto bg-white"
Expand All @@ -30,7 +32,13 @@ const DoctorNote = (props: DoctorNoteProps) => {
scrollableTarget="patient-notes-list"
>
{state.notes.map((note: any) => (
<PatientNoteCard note={note} key={note.id} />
<PatientNoteCard
state={state}
note={note}
key={note.id}
setReload={setReload}
disableEdit={disableEdit}
/>
))}
</InfiniteScroll>
) : (
Expand Down
16 changes: 11 additions & 5 deletions src/Components/Facility/PatientConsultationNotesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,15 @@ import request from "../../Utils/request/request";
interface PatientNotesProps {
state: PatientNoteStateType;
setState: any;
patientId: string;
facilityId: string;
reload?: boolean;
setReload?: any;
disableEdit?: boolean;
}

const pageSize = RESULTS_PER_PAGE_LIMIT;

const PatientConsultationNotesList = (props: PatientNotesProps) => {
const { state, setState, reload, setReload } = props;
const { state, setState, reload, setReload, disableEdit } = props;
const consultationId = useSlug("consultation") ?? "";

const [isLoading, setIsLoading] = useState(true);
Expand All @@ -28,7 +27,7 @@ const PatientConsultationNotesList = (props: PatientNotesProps) => {
setIsLoading(true);
const { data }: any = await request(routes.getPatientNotes, {
pathParams: {
patientId: props.patientId,
patientId: props.state.patientId,
},
query: {
consultation: consultationId,
Expand Down Expand Up @@ -81,7 +80,14 @@ const PatientConsultationNotesList = (props: PatientNotesProps) => {
);
}

return <DoctorNote state={state} handleNext={handleNext} />;
return (
<DoctorNote
state={state}
handleNext={handleNext}
setReload={setReload}
disableEdit={disableEdit}
/>
);
};

export default PatientConsultationNotesList;
262 changes: 236 additions & 26 deletions src/Components/Facility/PatientNoteCard.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,246 @@
import { relativeDate, formatDateTime, classNames } from "../../Utils/utils";
import { USER_TYPES_MAP } from "../../Common/constants";
import { PatientNotesModel } from "./models";
import {
PatientNoteStateType,
PatientNotesEditModel,
PatientNotesModel,
} from "./models";
import ButtonV2 from "../Common/components/ButtonV2";
import CareIcon from "../../CAREUI/icons/CareIcon";
import { useState } from "react";
import { Error, Success } from "../../Utils/Notifications";
import request from "../../Utils/request/request";
import routes from "../../Redux/api";
import DialogModal from "../Common/Dialog";
import { t } from "i18next";
import dayjs from "dayjs";
import Spinner from "../Common/Spinner";
import useAuthUser from "../../Common/hooks/useAuthUser";

const PatientNoteCard = ({
state,
note,
setReload,
disableEdit,
}: {
state: PatientNoteStateType;
note: PatientNotesModel;
setReload: any;
disableEdit?: boolean;
}) => {
const [isEditing, setIsEditing] = useState(false);
const [noteField, setNoteField] = useState(note.note);
const [showEditHistory, setShowEditHistory] = useState(false);
const [editHistory, setEditHistory] = useState<PatientNotesEditModel[]>([]);
const authUser = useAuthUser();

const fetchEditHistory = async () => {
const { res, data } = await request(routes.getPatientNoteEditHistory, {
pathParams: { patientId: state.patientId, noteId: note.id },
});
if (res?.status === 200) {
setEditHistory(data?.results ?? []);
}
};

const onUpdateNote = async () => {
if (noteField === note.note) {
setIsEditing(false);
return;
}
const payload = {
note: noteField,
};
if (!/\S+/.test(noteField)) {
Error({
msg: "Note Should Contain At Least 1 Character",
});
return;
}

const { res } = await request(routes.updatePatientNote, {
pathParams: { patientId: state.patientId, noteId: note.id },
body: payload,
});
if (res?.status === 200) {
Success({ msg: "Note updated successfully" });
setIsEditing(false);
setReload(true);
}
};

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
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>
<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 className="flex justify-between">
<div>
<div>
<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>
<div className="text-xs text-gray-600">
<div className="tooltip inline">
<span className="tooltip-text tooltip-bottom">
{formatDateTime(note.created_date)}
</span>
Created {relativeDate(note.created_date, true)}
</div>
</div>
{
// If last edited date is same as created date, then it is not edited
!dayjs(note.last_edited_date).isSame(
note.created_date,
"second"
) && (
<div className="flex">
<div
className="cursor-pointer text-xs text-gray-600"
onClick={() => {
fetchEditHistory();
setShowEditHistory(true);
}}
>
<div className="tooltip inline">
<span className="tooltip-text tooltip-bottom">
{formatDateTime(note.last_edited_date)}
</span>
Edited {relativeDate(note.last_edited_date, true)}
</div>
<CareIcon
icon="l-history"
className="ml-1 h-4 w-4 pt-[3px] text-primary-600"
/>
</div>
</div>
)
}
</div>

{!disableEdit &&
note.created_by_object.id === authUser.id &&
!isEditing && (
<ButtonV2
ghost
onClick={() => {
setIsEditing(true);
}}
>
<CareIcon icon="l-pen" className="h-5 w-5" />
</ButtonV2>
)}
</div>
{
<div className="mt-2">
{isEditing ? (
<div className="flex flex-col">
<textarea
rows={2}
className="h-20 w-full resize-none rounded-lg border border-gray-300 p-2"
value={noteField}
onChange={(e) => setNoteField(e.target.value)}
></textarea>
<div className="mt-2 flex justify-end gap-2">
<ButtonV2
className="py-1"
variant="secondary"
border
onClick={() => {
setIsEditing(false);
setNoteField(note.note);
}}
id="cancel-update-note-button"
>
<CareIcon icon="l-times-circle" className="h-5 w-5" />
Cancel
</ButtonV2>
<ButtonV2
className="py-1"
onClick={onUpdateNote}
id="update-note-button"
>
<CareIcon icon="l-check" className="h-5 w-5 text-white" />
Update Note
</ButtonV2>
</div>
</div>
) : (
<div className="text-sm text-gray-700">{noteField}</div>
)}
</div>
}
</div>
</div>
{showEditHistory && (
<DialogModal
show={showEditHistory}
onClose={() => setShowEditHistory(false)}
title={t("edit_history")}
>
<div>
<div className="mb-4">
<p className="text-md mt-1 text-gray-500">
Edit History for note
<strong> {note.id}</strong>
</p>
</div>
<div className="h-96 overflow-scroll">
{editHistory.length === 0 && (
<div className="flex h-full items-center justify-center">
<Spinner />
</div>
)}
{editHistory?.map((edit, index) => {
const isLast = index === editHistory.length - 1;
return (
<div
key={index}
className="my-2 flex flex-col justify-between rounded-lg border border-gray-300 p-4 py-2 transition-colors duration-200 hover:bg-gray-100"
>
<div className="flex">
<div className="grow">
<p className="text-sm font-medium text-gray-500">
{isLast ? "Created" : "Edited"} On
</p>
<p className="text-sm text-gray-900">
{formatDateTime(edit.edited_date)}
</p>
</div>
</div>
<div className="mt-2 grow">
<p className="text-sm font-medium text-gray-500">Note</p>
<p className="text-sm text-gray-900">{edit.note}</p>
</div>
</div>
);
})}
</div>
<div className="flex justify-end">
<ButtonV2
id="view-history-back-button"
variant="secondary"
onClick={() => {
setShowEditHistory(false);
}}
>
{t("close")}
</ButtonV2>
</div>
</div>
</DialogModal>
)}
</>
);
};

Expand Down
4 changes: 3 additions & 1 deletion src/Components/Facility/PatientNotesList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,9 @@ const PatientNotesList = (props: PatientNotesProps) => {
);
}

return <DoctorNote state={state} handleNext={handleNext} />;
return (
<DoctorNote state={state} handleNext={handleNext} setReload={setReload} />
);
};

export default PatientNotesList;
5 changes: 3 additions & 2 deletions src/Components/Facility/PatientNotesSlideover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export default function PatientNotesSlideover(props: PatientNotesProps) {
notes: [],
cPage: 1,
totalPages: 1,
patientId: props.patientId,
facilityId: props.facilityId,
};
const [state, setState] = useState(initialData);

Expand Down Expand Up @@ -163,10 +165,9 @@ export default function PatientNotesSlideover(props: PatientNotesProps) {
<PatientConsultationNotesList
state={state}
setState={setState}
facilityId={facilityId}
patientId={patientId}
reload={reload}
setReload={setReload}
disableEdit={!patientActive}
/>
<div className="relative mx-4 flex items-center">
<TextFormField
Expand Down
Loading
Loading