Skip to content

Commit

Permalink
Adds support for editing prescriptions (fixes #6340)
Browse files Browse the repository at this point in the history
  • Loading branch information
rithviknishad committed Sep 29, 2023
1 parent 1a174c2 commit 3208fac
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 13 deletions.
14 changes: 3 additions & 11 deletions src/Components/Medicine/CreatePrescriptionForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FieldError, RequiredFieldValidator } from "../Form/FieldValidators";
import { RequiredFieldValidator } from "../Form/FieldValidators";
import Form from "../Form/Form";
import { SelectFormField } from "../Form/FormFields/SelectFormField";
import TextAreaFormField from "../Form/FormFields/TextAreaFormField";
Expand All @@ -11,6 +11,7 @@ import NumericWithUnitsFormField from "../Form/FormFields/NumericWithUnitsFormFi
import { useTranslation } from "react-i18next";
import MedibaseAutocompleteFormField from "./MedibaseAutocompleteFormField";
import dayjs from "../../Utils/dayjs";
import { PrescriptionFormValidator } from "./validators";

export default function CreatePrescriptionForm(props: {
prescription: Prescription;
Expand Down Expand Up @@ -40,16 +41,7 @@ export default function CreatePrescriptionForm(props: {
}
}}
noPadding
validate={(form) => {
const errors: Partial<Record<keyof Prescription, FieldError>> = {};
errors.medicine_object = RequiredFieldValidator()(form.medicine_object);
errors.dosage = RequiredFieldValidator()(form.dosage);
if (form.is_prn)
errors.indicator = RequiredFieldValidator()(form.indicator);
if (!form.is_prn)
errors.frequency = RequiredFieldValidator()(form.frequency);
return errors;
}}
validate={PrescriptionFormValidator()}
className="max-w-3xl"
>
{(field) => (
Expand Down
158 changes: 158 additions & 0 deletions src/Components/Medicine/EditPrescriptionForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { useState } from "react";
import Form from "../Form/Form";
import { Prescription } from "./models";
import request from "../../Utils/request/request";
import routes from "../../Redux/api";
import * as Notification from "../../Utils/Notifications";
import useSlug from "../../Common/hooks/useSlug";
import { RequiredFieldValidator } from "../Form/FieldValidators";
import { useTranslation } from "react-i18next";
import { SelectFormField } from "../Form/FormFields/SelectFormField";
import NumericWithUnitsFormField from "../Form/FormFields/NumericWithUnitsFormField";
import {
PRESCRIPTION_FREQUENCIES,
PRESCRIPTION_ROUTES,
} from "./CreatePrescriptionForm";
import TextFormField from "../Form/FormFields/TextFormField";
import TextAreaFormField from "../Form/FormFields/TextAreaFormField";
import { EditPrescriptionFormValidator } from "./validators";

interface Props {
initial: Prescription;
onDone: (created: boolean) => void;
}

const handleSubmit = async (
consultation_external_id: string,
oldObj: Prescription,
{ discontinued_reason, ...newObj }: Prescription
) => {
const discontinue = await request(routes.discontinuePrescription, {
pathParams: { consultation_external_id, external_id: oldObj.id },
body: {
discontinued_reason: discontinued_reason
? `Edit: ${discontinued_reason}`
: "Edited",
},
});

if (discontinue.res?.status !== 200) {
Notification.Error({
msg: "Failed to discontinue previous prescription",
});
return;
}

const { res } = await request(routes.createPrescription, {
pathParams: { consultation_external_id },
body: {
...newObj,
// Forcing the medicine to be the same as the old one
medicine: oldObj.medicine_object?.id,
medicine_old: oldObj.medicine_old,
},
});

return res?.status === 201;
};

export default function EditPrescriptionForm(props: Props) {
const consultation = useSlug("consultation");
const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation();

return (
<Form<Prescription>
disabled={isLoading}
defaults={props.initial}
onCancel={() => props.onDone(false)}
onSubmit={async (obj) => {
setIsLoading(true);
const success = await handleSubmit(consultation, props.initial, obj);
setIsLoading(false);

if (success) {
props.onDone(true);
}
}}
noPadding
validate={EditPrescriptionFormValidator(props.initial)}
>
{(field) => (
<>
<TextAreaFormField
label={t("reason_for_edit")}
{...field("discontinued_reason")}
/>

<div className="flex items-center gap-4">
<SelectFormField
className="flex-1"
label={t("route")}
{...field("route")}
options={PRESCRIPTION_ROUTES}
optionLabel={(key) => t("PRESCRIPTION_ROUTE_" + key)}
optionValue={(key) => key}
/>
<NumericWithUnitsFormField
className="flex-1"
label={t("dosage")}
{...field("dosage", RequiredFieldValidator())}
required
units={["mg", "g", "ml", "drop(s)", "ampule(s)", "tsp"]}
min={0}
/>
</div>

{props.initial.is_prn ? (
<>
<TextFormField
label={t("indicator")}
{...field("indicator", RequiredFieldValidator())}
required
/>
<TextFormField
label={t("max_dosage_24_hrs")}
type="number"
min={0}
{...field("max_dosage")}
/>
<SelectFormField
label={t("min_time_bw_doses")}
{...field("min_hours_between_doses")}
options={[1, 2, 3, 6, 12, 24]}
optionLabel={(hours) => `${hours} hrs.`}
optionValue={(hours) => hours}
position="above"
/>
</>
) : (
<div className="flex items-center gap-4">
<SelectFormField
position="above"
className="flex-1"
label={t("frequency")}
{...field("frequency", RequiredFieldValidator())}
required
options={Object.entries(PRESCRIPTION_FREQUENCIES)}
optionLabel={([key]) =>
t("PRESCRIPTION_FREQUENCY_" + key.toUpperCase())
}
optionValue={([key]) => key}
/>
<TextFormField
className="flex-1"
label={t("days")}
type="number"
min={0}
{...field("days")}
/>
</div>
)}

<TextAreaFormField label={t("notes")} {...field("notes")} />
</>
)}
</Form>
);
}
45 changes: 45 additions & 0 deletions src/Components/Medicine/PrescriptionAdministrationsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
formatTime,
} from "../../Utils/utils";
import useRangePagination from "../../Common/hooks/useRangePagination";
import EditPrescriptionForm from "./EditPrescriptionForm";

interface DateRange {
start: Date;
Expand Down Expand Up @@ -299,6 +300,7 @@ const PrescriptionRow = ({ prescription, ...props }: PrescriptionRowProps) => {
const { t } = useTranslation();
// const [showActions, setShowActions] = useState(false);
const [showDetails, setShowDetails] = useState(false);
const [showEdit, setShowEdit] = useState(false);
const [showAdminister, setShowAdminister] = useState(false);
const [showDiscontinue, setShowDiscontinue] = useState(false);
const [administrations, setAdministrations] =
Expand Down Expand Up @@ -382,6 +384,21 @@ const PrescriptionRow = ({ prescription, ...props }: PrescriptionRowProps) => {
<CareIcon className="care-l-ban text-lg" />
{t("discontinue")}
</Submit>
<Submit
disabled={
prescription.discontinued ||
prescription.prescription_type === "DISCHARGE"
}
variant="secondary"
border
onClick={() => {
setShowDetails(false);
setShowEdit(true);
}}
>
<CareIcon icon="l-pen" className="text-lg" />
{t("edit")}
</Submit>
<Submit
disabled={
prescription.discontinued ||
Expand All @@ -396,6 +413,34 @@ const PrescriptionRow = ({ prescription, ...props }: PrescriptionRowProps) => {
</div>
</DialogModal>
)}
{showEdit && (
<DialogModal
onClose={() => setShowEdit(false)}
show={showEdit}
title={`${t("edit")} ${t(
prescription.is_prn ? "prn_prescription" : "prescription_medication"
)}: ${
prescription.medicine_object?.name ?? prescription.medicine_old
}`}
description={
<div className="mt-2 flex w-full justify-start gap-2 text-warning-500">
<CareIcon icon="l-info-circle" className="text-base" />
<span>{t("edit_caution_note")}</span>
</div>
}
className="w-full max-w-3xl lg:min-w-[600px]"
>
<EditPrescriptionForm
initial={prescription}
onDone={(success) => {
setShowEdit(false);
if (success) {
props.refetch();
}
}}
/>
</DialogModal>
)}
<tr
className={classNames(
"group border-separate border border-gray-300 bg-gray-100 transition-all duration-200 ease-in-out hover:border-primary-300 hover:bg-primary-100"
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Medicine/models.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { PerformedByModel } from "../HCX/misc";

interface BasePrescription {
readonly id?: string;
readonly id: string;
medicine?: string;
medicine_object?: MedibaseMedicine;
medicine_old?: string;
Expand Down
49 changes: 49 additions & 0 deletions src/Components/Medicine/validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { FieldError, RequiredFieldValidator } from "../Form/FieldValidators";
import { FormErrors } from "../Form/Utils";
import { Prescription } from "./models";

export const PrescriptionFormValidator = () => {
return (form: Prescription): FormErrors<Prescription> => {
const errors: Partial<Record<keyof Prescription, FieldError>> = {};
errors.medicine_object = RequiredFieldValidator()(form.medicine_object);
errors.dosage = RequiredFieldValidator()(form.dosage);
if (form.is_prn)
errors.indicator = RequiredFieldValidator()(form.indicator);
if (!form.is_prn)
errors.frequency = RequiredFieldValidator()(form.frequency);
return errors;
};
};

export const EditPrescriptionFormValidator = (old: Prescription) => {
return (form: Prescription): FormErrors<Prescription> => {
const errors = PrescriptionFormValidator()(form);

if (comparePrescriptions(old, form)) {
errors.$all = "No changes made";
}

return errors;
};
};

const PRESCRIPTION_COMPARE_FIELDS: (keyof Prescription)[] = [
"medicine",
"days",
"discontinued",
"dosage",
"frequency",
"indicator",
"is_prn",
"max_dosage",
"min_hours_between_doses",
"prescription_type",
"route",
];

export const comparePrescriptions = (a: Prescription, b: Prescription) => {
return (
PRESCRIPTION_COMPARE_FIELDS.every((field) => a[field] === b[field]) &&
a.medicine_object?.id === b.medicine_object?.id
);
};
4 changes: 3 additions & 1 deletion src/Locale/en/Medicine.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
"last_administered": "Last administered",
"modification_caution_note": "No modifications possible once added",
"discontinue_caution_note": "Are you sure you want to discontinue this prescription?",
"edit_caution_note": "A new prescription will be added to the consultation with the edited details and the current prescription will be discontinued.",
"reason_for_discontinuation": "Reason for discontinuation",
"reason_for_edit": "Reason for edit",
"PRESCRIPTION_ROUTE_ORAL": "Oral",
"PRESCRIPTION_ROUTE_IV": "IV",
"PRESCRIPTION_ROUTE_IM": "IM",
Expand All @@ -47,4 +49,4 @@
"PRESCRIPTION_FREQUENCY_Q4H": "4th hourly",
"PRESCRIPTION_FREQUENCY_QOD": "Alternate day",
"PRESCRIPTION_FREQUENCY_QWK": "Once a week"
}
}
5 changes: 5 additions & 0 deletions src/Redux/api.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IConfig } from "../Common/hooks/useConfig";
import { AssetData } from "../Components/Assets/AssetTypes";
import { LocationModel } from "../Components/Facility/models";
import { Prescription } from "../Components/Medicine/models";
import { UserModel } from "../Components/Users/models";
import { PaginatedResponse } from "../Utils/request/types";

Expand Down Expand Up @@ -972,6 +973,8 @@ const routes = {
createPrescription: {
path: "/api/v1/consultation/{consultation_external_id}/prescriptions/",
method: "POST",
TBody: Type<Prescription>(),
TRes: Type<Prescription>(),
},

listAdministrations: {
Expand All @@ -997,6 +1000,8 @@ const routes = {
discontinuePrescription: {
path: "/api/v1/consultation/{consultation_external_id}/prescriptions/{external_id}/discontinue/",
method: "POST",
TBody: Type<{ discontinued_reason: string }>(),
TRes: Type<Record<string, never>>(),
},

// HCX Endpoints
Expand Down

0 comments on commit 3208fac

Please sign in to comment.