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

Adds support for Doctors Log Update round type (v1) #7839

Merged
merged 11 commits into from
May 28, 2024
18 changes: 18 additions & 0 deletions src/Components/Facility/ConsultationDetails/Events/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import routes from "../../../../Redux/api";
import request from "../../../../Utils/request/request";
import { UserBareMinimum } from "../../../Users/models";

export type Type = {
Expand Down Expand Up @@ -28,3 +30,19 @@ export type EventGeneric = {
};

// TODO: Once event types are finalized, define specific types for each event

let cachedEventTypes: Type[] | null = null;

export const fetchEventTypeByName = async (name: Type["name"]) => {
if (!cachedEventTypes) {
const { data } = await request(routes.listEventTypes, {
query: { limit: 100 },
});

if (data?.results) {
cachedEventTypes = data.results;
}
}

return cachedEventTypes?.find((t) => t.name === name);
};
219 changes: 183 additions & 36 deletions src/Components/Patient/DailyRounds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,17 @@ import routes from "../../Redux/api";
import { Scribe } from "../Scribe/Scribe";
import { DAILY_ROUND_FORM_SCRIBE_DATA } from "../Scribe/formDetails";
import { DailyRoundsModel } from "./models";
import { fetchEventTypeByName } from "../Facility/ConsultationDetails/Events/types";
import InvestigationBuilder from "../Common/prescription-builder/InvestigationBuilder";
import { FieldErrorText } from "../Form/FormFields/FormField";
import { error } from "@pnotify/core";
import { useTranslation } from "react-i18next";
import PrescriptionBuilder from "../Medicine/PrescriptionBuilder";
import { EditDiagnosesBuilder } from "../Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder";
import {
ConditionVerificationStatuses,
ConsultationDiagnosis,
} from "../Diagnosis/types";
import { EncounterSymptomsBuilder } from "../Symptoms/SymptomsBuilder";
import { FieldLabel } from "../Form/FormFields/FormField";
const Loading = lazy(() => import("../Common/Loading"));
Expand All @@ -47,6 +58,8 @@ const initForm: any = {
taken_at: null,
rounds_type: "NORMAL",
systolic: null,
investigations: [],
investigations_dirty: false,
diastolic: null,
pulse: null,
resp: null,
Expand Down Expand Up @@ -97,6 +110,7 @@ const DailyRoundsFormReducer = (state = initialState, action: any) => {
};

export const DailyRounds = (props: any) => {
const { t } = useTranslation();
const { goBack } = useAppHistory();
const { facilityId, patientId, consultationId, id } = props;
const [state, dispatch] = useAutoSaveReducer<any>(
Expand All @@ -113,6 +127,7 @@ export const DailyRounds = (props: any) => {
...initForm,
action: "",
});
const [diagnoses, setDiagnoses] = useState<ConsultationDiagnosis[]>();
const headerText = !id ? "Add Consultation Update" : "Info";
const buttonText = !id ? "Save" : "Continue";

Expand All @@ -124,6 +139,7 @@ export const DailyRounds = (props: any) => {
"bp",
"pulse",
"resp",
"investigations",
"ventilator_spo2",
"rhythm",
"rhythm_detail",
Expand All @@ -132,6 +148,7 @@ export const DailyRounds = (props: any) => {

const fetchRoundDetails = useCallback(async () => {
setIsLoading(true);
fetchEventTypeByName("");
let formData: any = initialData;
if (id) {
const { data } = await request(routes.getDailyReport, {
Expand Down Expand Up @@ -163,6 +180,13 @@ export const DailyRounds = (props: any) => {
setPatientName(data.name!);
setFacilityName(data.facility_object!.name);
setConsultationSuggestion(data.last_consultation?.suggestion);
setDiagnoses(
data.last_consultation?.diagnoses?.sort(
(a: ConsultationDiagnosis, b: ConsultationDiagnosis) =>
ConditionVerificationStatuses.indexOf(a.verification_status) -
ConditionVerificationStatuses.indexOf(b.verification_status),
),
);
setPreviousReviewInterval(
Number(data.last_consultation?.review_interval),
);
Expand All @@ -174,7 +198,11 @@ export const DailyRounds = (props: any) => {
...initialData,
action: getAction,
});
formData = { ...formData, ...{ action: getAction } };
formData = {
...formData,
action: getAction,
investigations: data.last_consultation?.investigation ?? [],
};
}
} else {
setPatientName("");
Expand Down Expand Up @@ -207,6 +235,33 @@ export const DailyRounds = (props: any) => {
}
return;
}

case "investigations": {
for (const investigation of state.form.investigations) {
if (!investigation.type?.length) {
errors[field] = "Investigation field can not be empty";
invalidForm = true;
break;
}
if (
investigation.repetitive &&
!investigation.frequency?.replace(/\s/g, "").length
) {
errors[field] = "Frequency field cannot be empty";
invalidForm = true;
break;
}
if (
!investigation.repetitive &&
!investigation.time?.replace(/\s/g, "").length
) {
errors[field] = "Time field cannot be empty";
invalidForm = true;
break;
}
}
return;
}
default:
return;
}
Expand All @@ -220,6 +275,25 @@ export const DailyRounds = (props: any) => {
const validForm = validateForm();
if (validForm) {
setIsLoading(true);

if (
state.form.rounds_type === "DOCTORS_LOG" &&
state.form.investigations_dirty
) {
const { error: investigationError } = await request(
routes.partialUpdateConsultation,
{
body: { investigation: state.form.investigations },
pathParams: { id: consultationId },
},
);

if (investigationError) {
Notification.Error({ msg: error });
return;
}
}

let data: DailyRoundsModel = {
rounds_type: state.form.rounds_type,
patient_category: state.form.patient_category,
Expand Down Expand Up @@ -282,14 +356,24 @@ export const DailyRounds = (props: any) => {
setIsLoading(false);
if (obj) {
dispatch({ type: "set_form", form: initForm });
Notification.Success({
msg: `${obj.rounds_type === "VENTILATOR" ? "Critical Care" : capitalize(obj.rounds_type)} Log Updates details created successfully`,
});
if (["NORMAL", "TELEMEDICINE"].includes(state.form.rounds_type)) {
Notification.Success({
msg: `${state.form.rounds_type === "NORMAL" ? "Normal" : "Tele-medicine"} log update created successfully`,
});
navigate(
`/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}`,
);
} else if (state.form.rounds_type === "DOCTORS_LOG") {
Notification.Success({
msg: "Doctors log update created successfully",
});
navigate(
`/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}`,
);
} else {
Notification.Success({
msg: "Critical Care log update created successfully",
});
navigate(
`/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}/daily_rounds/${obj.id}/update`,
);
Expand All @@ -300,10 +384,16 @@ export const DailyRounds = (props: any) => {
};

const handleFormFieldChange = (event: FieldChangeEvent<unknown>) => {
dispatch({
type: "set_form",
form: { ...state.form, [event.name]: event.value },
});
const form = {
...state.form,
[event.name]: event.value,
};

if (event.name === "investigations") {
form["investigations_dirty"] = true;
}

dispatch({ type: "set_form", form });
};

const field = (name: string) => {
Expand Down Expand Up @@ -390,6 +480,7 @@ export const DailyRounds = (props: any) => {
options={[
...[
{ id: "NORMAL", text: "Normal" },
{ id: "DOCTORS_LOG", text: "Doctor's Log Update" },
{ id: "VENTILATOR", text: "Critical Care" },
],
...(consultationSuggestion == "DC"
Expand Down Expand Up @@ -426,34 +517,40 @@ export const DailyRounds = (props: any) => {
<EncounterSymptomsBuilder />
</div>

<SelectFormField
{...field("action")}
label="Action"
options={TELEMEDICINE_ACTIONS}
optionLabel={(option) => option.desc}
optionValue={(option) => option.text}
value={prevAction}
onChange={(event) => {
handleFormFieldChange(event);
setPreviousAction(event.value);
}}
/>
{state.form.rounds_type !== "DOCTORS_LOG" && (
<>
<SelectFormField
{...field("action")}
label="Action"
options={TELEMEDICINE_ACTIONS}
optionLabel={(option) => option.desc}
optionValue={(option) => option.text}
value={prevAction}
onChange={(event) => {
handleFormFieldChange(event);
setPreviousAction(event.value);
}}
/>

<SelectFormField
{...field("review_interval")}
label="Review After"
labelSuffix={getExpectedReviewTime()}
options={REVIEW_AT_CHOICES}
optionLabel={(option) => option.text}
optionValue={(option) => option.id}
value={prevReviewInterval}
onChange={(event) => {
handleFormFieldChange(event);
setPreviousReviewInterval(Number(event.value));
}}
/>
<SelectFormField
{...field("review_interval")}
label="Review After"
labelSuffix={getExpectedReviewTime()}
options={REVIEW_AT_CHOICES}
optionLabel={(option) => option.text}
optionValue={(option) => option.id}
value={prevReviewInterval}
onChange={(event) => {
handleFormFieldChange(event);
setPreviousReviewInterval(Number(event.value));
}}
/>
</>
)}

{["NORMAL", "TELEMEDICINE"].includes(state.form.rounds_type) && (
{["NORMAL", "TELEMEDICINE", "DOCTORS_LOG"].includes(
state.form.rounds_type,
) && (
<>
<h3 className="mb-6 md:col-span-2">Vitals</h3>

Expand Down Expand Up @@ -572,6 +669,53 @@ export const DailyRounds = (props: any) => {
/>
</>
)}

{state.form.rounds_type === "DOCTORS_LOG" && (
<>
<div className="flex flex-col gap-10 divide-y-2 divide-dashed divide-gray-600 border-t-2 border-dashed border-gray-600 pt-6 md:col-span-2">
<div>
<h3 className="my-4 text-lg font-semibold">
{t("investigations")}
</h3>
<InvestigationBuilder
investigations={state.form.investigations}
setInvestigations={(investigations) => {
handleFormFieldChange({
name: "investigations",
value: investigations,
});
}}
/>
<FieldErrorText error={state.errors.investigation} />
</div>
<div>
<h3 className="mb-4 mt-8 text-lg font-semibold">
{t("prescription_medications")}
</h3>
<PrescriptionBuilder />
</div>
<div>
<h3 className="mb-4 mt-8 text-lg font-semibold">
{t("prn_prescriptions")}
</h3>
<PrescriptionBuilder is_prn />
</div>
<div>
<h3 className="mb-4 mt-8 text-lg font-semibold">
{t("diagnosis")}
</h3>
{/* */}
{diagnoses ? (
<EditDiagnosesBuilder value={diagnoses} />
) : (
<div className="flex animate-pulse justify-center py-4 text-center font-medium text-gray-800">
Fetching existing diagnosis of patient...
</div>
)}
</div>
</div>
</>
)}
</div>

<div className="mt-4 flex flex-col-reverse justify-end gap-2 md:flex-row">
Expand All @@ -580,11 +724,14 @@ export const DailyRounds = (props: any) => {
disabled={
buttonText === "Save" &&
formFields.every(
(field: string) => state.form[field] == initialData[field],
(field: string) =>
JSON.stringify(state.form[field]) ===
JSON.stringify(initialData[field]),
) &&
(state.form.temperature == initialData.temperature ||
isNaN(state.form.temperature)) &&
state.form.rounds_type !== "VENTILATOR"
state.form.rounds_type !== "VENTILATOR" &&
state.form.rounds_type !== "DOCTORS_LOG"
}
onClick={(e) => handleSubmit(e)}
label={buttonText}
Expand Down
1 change: 1 addition & 0 deletions src/Components/Patient/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ export interface DailyRoundsOutput {

export const DailyRoundTypes = [
"NORMAL",
"DOCTORS_LOG",
"VENTILATOR",
"AUTOMATED",
"TELEMEDICINE",
Expand Down
Loading
Loading