From dabf8cce27e7b21b24dea0aea9bdcbe04314c20b Mon Sep 17 00:00:00 2001 From: Gigin George Date: Mon, 5 Aug 2024 07:16:27 +0530 Subject: [PATCH 01/17] Update stale, to ignore backlog Issues & PRs (#8249) --- .github/workflows/stale.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 8964b0c8e57..736ce33d87d 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -16,8 +16,8 @@ jobs: stale-issue-message: "Hi, @coronasafe/care-frontend-maintainers, This issue has been automatically marked as stale because it has not had any recent activity." stale-pr-message: "Hi, This pr has been automatically marked as stale because it has not had any recent activity. It will be automatically closed if no further activity occurs for 7 more days. Thank you for your contributions." close-pr-message: "Hi, @coronasafe/care-frontend-maintainers, This PR has been automatically closed due to inactivity. Thank you for your contributions. Feel free to re-open the PR." - exempt-issue-labels: "blocked,waiting for related PR,waiting for back end,help wanted,work-in-progress,In Progress,wishlist,EPIC" - exempt-pr-labels: "tested,needs testing,need Review,waiting for related PR,waiting for back end,help wanted,blocked,work-in-progress,In Progress" + exempt-issue-labels: "blocked,waiting for related PR,waiting for back end,help wanted,work-in-progress,In Progress,wishlist,EPIC,backlog" + exempt-pr-labels: "tested,needs testing,need Review,waiting for related PR,waiting for back end,help wanted,blocked,work-in-progress,In Progress,backlog" days-before-issue-stale: 14 days-before-pr-stale: 7 days-before-issue-close: -1 From 8096a3c14f5955517f2dea956638baf811abe327 Mon Sep 17 00:00:00 2001 From: Kushal Bhana <68599557+kushalbhana@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:02:09 +0530 Subject: [PATCH 02/17] scroll to mandatory empty fields (#8263) * scroll to mandatory empty fields * fixing the issue of auto scroll --- src/Components/Patient/DailyRounds.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index 321536e80f5..d451513c9bc 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -45,6 +45,7 @@ import { EncounterSymptomsBuilder } from "../Symptoms/SymptomsBuilder"; import { FieldLabel } from "../Form/FormFields/FormField"; import useAuthUser from "../../Common/hooks/useAuthUser"; import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField"; +import { scrollTo } from "../../Utils/utils"; const Loading = lazy(() => import("../Common/Loading")); @@ -231,6 +232,7 @@ export const DailyRounds = (props: any) => { if (!state.form[field]) { errors[field] = "Please select a category"; invalidForm = true; + scrollTo("patientCategory"); } return; case "bp": { @@ -238,6 +240,7 @@ export const DailyRounds = (props: any) => { if (error) { errors.bp = error; invalidForm = true; + scrollTo("bloodPressure"); } return; } @@ -524,6 +527,7 @@ export const DailyRounds = (props: any) => { {...field("patient_category")} required label="Category" + id="patientCategory" /> @@ -589,7 +593,7 @@ export const DailyRounds = (props: any) => { <>

Vitals

- + Date: Wed, 7 Aug 2024 12:02:32 +0530 Subject: [PATCH 03/17] Fix: Added online status to Doctor Connect (#8261) * Added online status * Added utility fn --- src/Components/Facility/DoctorVideoSlideover.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Components/Facility/DoctorVideoSlideover.tsx b/src/Components/Facility/DoctorVideoSlideover.tsx index aacf441be6f..53a197734dc 100644 --- a/src/Components/Facility/DoctorVideoSlideover.tsx +++ b/src/Components/Facility/DoctorVideoSlideover.tsx @@ -3,7 +3,7 @@ import SlideOver from "../../CAREUI/interactive/SlideOver"; import { UserAssignedModel } from "../Users/models"; import { SkillObjectModel } from "../Users/models"; import CareIcon, { IconName } from "../../CAREUI/icons/CareIcon"; -import { classNames, relativeTime } from "../../Utils/utils"; +import { classNames, isUserOnline, relativeTime } from "../../Utils/utils"; import useAuthUser from "../../Common/hooks/useAuthUser"; import { triggerGoal } from "../../Integrations/Plausible"; import { Warn } from "../../Utils/Notifications"; @@ -241,9 +241,14 @@ function UserListItem({ user }: { user: UserAnnotatedWithGroup }) {
{ // Show online icon based on last_login - user.last_login && - Number(new Date()) - Number(new Date(user.last_login)) < 60000 ? ( - + user.last_login && isUserOnline(user) ? ( + <> + + + ) : ( ) From a169f02e8e2bccfea9f70167aacb01f8d3242afa Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 7 Aug 2024 12:55:35 +0530 Subject: [PATCH 04/17] Camera Feed: Ensure camera moves everytime the preset is clicked (#8254) --- .../Facility/ConsultationDetails/ConsultationFeedTab.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx index 34931b303f8..7b266e91639 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx @@ -167,7 +167,14 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { result: "success", }); setHasMoved(false); - setPreset(value); + // Voluntarily copying to trigger change of reference of the position attribute, so that the useEffect of CameraFeed that handles the moves gets triggered. + setPreset({ + ...value, + meta: { + ...value.meta, + position: { ...value.meta.position }, + }, + }); }} /> {isUpdatingPreset ? ( From 4c882b41b1073b8c629eafae44b5db302d167bf9 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 7 Aug 2024 12:55:58 +0530 Subject: [PATCH 05/17] Live Feed: Move to last accessed preset instead of last created (#8255) --- .../ConsultationFeedTab.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx index 7b266e91639..d8d4f8b3d28 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationFeedTab.tsx @@ -59,12 +59,18 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { return; } - const preset = data.results.find( + const presets = data.results.filter( (obj) => obj.asset_object.meta?.asset_type === "CAMERA" && obj.meta.type !== "boundary", ); + const lastPresetId = sessionStorage.getItem( + getFeedPresetKey(props.consultationId), + ); + const preset = + presets.find((obj) => obj.id === lastPresetId) ?? presets[0]; + if (preset) { setPreset(preset); setAsset(preset.asset_object); @@ -105,6 +111,13 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { } }, [!!bed, loading, !!asset, divRef.current]); + useEffect(() => { + const feedPresetKey = getFeedPresetKey(props.consultationId); + if (preset) { + sessionStorage.setItem(feedPresetKey, preset.id); + } + }, [preset, props.consultationId]); + if (loading) { return ; } @@ -212,3 +225,7 @@ export const ConsultationFeedTab = (props: ConsultationTabProps) => { ); }; + +const getFeedPresetKey = (consultationId: string) => { + return `encounterFeedPreset[${consultationId}]`; +}; From eb5e6ebfe32887e04d42f1ac1fef2293bbc3694a Mon Sep 17 00:00:00 2001 From: Harsh Renose <70405311+renoseHarsh@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:57:08 +0530 Subject: [PATCH 06/17] Increased Form Size & Updated Indicator Field (#8244) --- src/Components/Medicine/ManagePrescriptions.tsx | 2 +- src/Components/Medicine/PrescriptionDetailCard.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Components/Medicine/ManagePrescriptions.tsx b/src/Components/Medicine/ManagePrescriptions.tsx index fd335ce19fb..a5ae50813a2 100644 --- a/src/Components/Medicine/ManagePrescriptions.tsx +++ b/src/Components/Medicine/ManagePrescriptions.tsx @@ -12,7 +12,7 @@ export default function ManagePrescriptions() { return (
diff --git a/src/Components/Medicine/PrescriptionDetailCard.tsx b/src/Components/Medicine/PrescriptionDetailCard.tsx index 0bc75d66a72..83e320730a4 100644 --- a/src/Components/Medicine/PrescriptionDetailCard.tsx +++ b/src/Components/Medicine/PrescriptionDetailCard.tsx @@ -178,19 +178,19 @@ export default function PrescriptionDetailCard({ {prescription.dosage_type === "PRN" ? ( <> {prescription.indicator} {prescription.max_dosage} {prescription.min_hours_between_doses && From 91ef1cf112aba379fb4eb86f09ef8f9919a0d799 Mon Sep 17 00:00:00 2001 From: Vishvam Moliya <96417040+thevishvammoliya@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:57:34 +0530 Subject: [PATCH 07/17] =?UTF-8?q?Fixed:=20Misalignment=20of=20placeholder?= =?UTF-8?q?=20text=20inside=20date=20component=20in=20the=20=E2=80=A6=20(#?= =?UTF-8?q?8240)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed: Misalignment of placeholder text inside date component in the add user page #8239 * Update src/Components/Common/DateInputV2.tsx Co-authored-by: Rithvik Nishad --------- Co-authored-by: Rithvik Nishad --- src/Components/Common/DateInputV2.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Common/DateInputV2.tsx b/src/Components/Common/DateInputV2.tsx index 9bb10b1951b..2e9f99de9bc 100644 --- a/src/Components/Common/DateInputV2.tsx +++ b/src/Components/Common/DateInputV2.tsx @@ -256,7 +256,7 @@ const DateInputV2: React.FC = ({ type="text" readOnly disabled={disabled} - className={`cui-input-base cursor-pointer !px-2 disabled:cursor-not-allowed ${className}`} + className={`cui-input-base cursor-pointer disabled:cursor-not-allowed ${className}`} placeholder={placeholder ?? t("select_date")} value={value && dayjs(value).format("DD/MM/YYYY")} /> From 203afd204a473c389c6b4d2d26b8e88600abc5e2 Mon Sep 17 00:00:00 2001 From: Harsh Renose <70405311+renoseHarsh@users.noreply.github.com> Date: Wed, 7 Aug 2024 12:57:57 +0530 Subject: [PATCH 08/17] Fixed: Transfer patient pop-up behavior (#8245) * Updated Pop Close * Updaed Pop-up Close for Transfer --- .../Facility/DuplicatePatientDialog.tsx | 5 ++-- src/Components/Patient/PatientRegister.tsx | 24 +++++++++++++++---- 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/src/Components/Facility/DuplicatePatientDialog.tsx b/src/Components/Facility/DuplicatePatientDialog.tsx index 7450d9fd8f1..b948ad91fa3 100644 --- a/src/Components/Facility/DuplicatePatientDialog.tsx +++ b/src/Components/Facility/DuplicatePatientDialog.tsx @@ -7,13 +7,12 @@ interface Props { patientList: Array; handleOk: (action: string) => void; handleCancel: () => void; - isNew: boolean; } const tdClass = "border border-secondary-400 p-2 text-left"; const DuplicatePatientDialog = (props: Props) => { - const { patientList, handleOk, handleCancel, isNew } = props; + const { patientList, handleOk, handleCancel } = props; const [action, setAction] = useState(""); return ( @@ -118,7 +117,7 @@ const DuplicatePatientDialog = (props: Props) => { { }); const [careExtId, setCareExtId] = useState(""); const [formField, setFormField] = useState(); + const [resetNum, setResetNum] = useState(false); const [isDistrictLoading, setIsDistrictLoading] = useState(false); const [isLocalbodyLoading, setIsLocalbodyLoading] = useState(false); const [isWardLoading, setIsWardLoading] = useState(false); @@ -1003,21 +1004,29 @@ export const PatientRegister = (props: PatientRegisterProps) => { { + handleDialogClose("close"); + setResetNum(true); + }} /> )} {statusDialog.transfer && ( handleDialogClose("back")} + onClose={() => { + setResetNum(true); + handleDialogClose("close"); + }} title="Patient Transfer Form" className="max-w-md md:min-w-[600px]" > handleDialogClose("close")} - handleCancel={() => handleDialogClose("back")} + handleCancel={() => { + setResetNum(true); + handleDialogClose("close"); + }} facilityId={facilityId} /> @@ -1134,6 +1143,13 @@ export const PatientRegister = (props: PatientRegisterProps) => { > {(field) => { if (!formField) setFormField(field); + if (resetNum) { + field("phone_number").onChange({ + name: "phone_number", + value: "+91", + }); + setResetNum(false); + } return ( <>
From f3dd93dab3a65765c3cd1868f3cc900af5ab3874 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Wed, 7 Aug 2024 12:58:09 +0530 Subject: [PATCH 09/17] Show weight and height as unspecified rather than 0 (#8248) --- .../ConsultationUpdatesTab.tsx | 29 +++++++++++++------ src/Components/Facility/ConsultationForm.tsx | 22 +++++++++----- 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx index 22194b410b7..d00b77d9bca 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx @@ -555,24 +555,35 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
Weight {" - "} - {props.consultationData.weight ?? "-"} Kg + {props.consultationData.weight + ? `${props.consultationData.weight} kg` + : "Unspecified"}
Height {" - "} - {props.consultationData.height ?? "-"} cm + {props.consultationData.height + ? `${props.consultationData.height} cm` + : "Unspecified"}
Body Surface Area {" - "} - - {Math.sqrt( - (Number(props.consultationData.weight) * - Number(props.consultationData.height)) / - 3600, - ).toFixed(2)}{" "} - m2 + + {props.consultationData.weight && + props.consultationData.height ? ( + <> + {Math.sqrt( + (Number(props.consultationData.weight) * + Number(props.consultationData.height)) / + 3600, + ).toFixed(2)} + m2 + + ) : ( + "Unspecified" + )}
diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index dc496d599f7..40d9741d712 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -386,8 +386,8 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { admitted: data.admitted ? String(data.admitted) : "false", admitted_to: data.admitted_to ? data.admitted_to : "", category: data.category - ? (PATIENT_CATEGORIES.find((i) => i.text === data.category)?.id ?? - "") + ? PATIENT_CATEGORIES.find((i) => i.text === data.category)?.id ?? + "" : "", patient_no: data.patient_no ?? "", OPconsultation: data.consultation_notes, @@ -1084,12 +1084,18 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => {
Body Surface Area - {Math.sqrt( - (Number(state.form.weight) * - Number(state.form.height)) / - 3600, - ).toFixed(2)} - m2 + {state.form.weight && state.form.height ? ( + <> + {Math.sqrt( + (Number(state.form.weight) * + Number(state.form.height)) / + 3600, + ).toFixed(2)} + m2 + + ) : ( + "Not specified" + )}
From caa411dcf93ee1ceb419ff7aa5b00183b07809e8 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Wed, 7 Aug 2024 20:13:12 +0530 Subject: [PATCH 10/17] Cypress: fix incorrect patient category select's id (#8267) --- cypress/pageobject/Patient/PatientLogupdate.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cypress/pageobject/Patient/PatientLogupdate.ts b/cypress/pageobject/Patient/PatientLogupdate.ts index bc141c04984..ac1bd6a4991 100644 --- a/cypress/pageobject/Patient/PatientLogupdate.ts +++ b/cypress/pageobject/Patient/PatientLogupdate.ts @@ -16,7 +16,7 @@ class PatientLogupdate { } selectPatientCategory(category: string) { - cy.clickAndSelectOption("#patient_category", category); + cy.clickAndSelectOption("#patientCategory", category); } typePhysicalExamination(examination: string) { From b552047d64a1b8fac950eb4ba5d5a39bc08b0bef Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Thu, 8 Aug 2024 16:20:26 +0530 Subject: [PATCH 11/17] =?UTF-8?q?Adds=20support=20for=20printing=20?= =?UTF-8?q?=F0=9F=96=A8=EF=B8=8F=20prescriptions=20=F0=9F=92=8A=20(#8259)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adds support for printing prescriptions * Improve print preview layout * add links to reach the print page * update title of print output * Updated prescription print preview as per requirements * disable print if empty; add titration instructions; improve layout * remove todo comments :) * update disable logic --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- src/CAREUI/misc/PrintPreview.tsx | 36 +++ .../Medicine/ManagePrescriptions.tsx | 10 +- .../MedicineAdministrationSheet/index.tsx | 44 ++- src/Components/Medicine/PrintPreview.tsx | 271 ++++++++++++++++++ src/Locale/en/Common.json | 5 +- src/Locale/en/Consultation.json | 6 + src/Locale/en/Medicine.json | 4 +- src/Routers/routes/ConsultationRoutes.tsx | 3 + 8 files changed, 360 insertions(+), 19 deletions(-) create mode 100644 src/CAREUI/misc/PrintPreview.tsx create mode 100644 src/Components/Medicine/PrintPreview.tsx diff --git a/src/CAREUI/misc/PrintPreview.tsx b/src/CAREUI/misc/PrintPreview.tsx new file mode 100644 index 00000000000..243826c7337 --- /dev/null +++ b/src/CAREUI/misc/PrintPreview.tsx @@ -0,0 +1,36 @@ +import { ReactNode } from "react"; +import ButtonV2 from "../../Components/Common/components/ButtonV2"; +import CareIcon from "../icons/CareIcon"; +import { classNames } from "../../Utils/utils"; +import Page from "../../Components/Common/components/Page"; + +type Props = { + children: ReactNode; + disabled?: boolean; + className?: string; + title: string; +}; + +export default function PrintPreview(props: Props) { + return ( + +
+
+ window.print()}> + + Print + +
+ +
+
+ {props.children} +
+
+
+
+ ); +} diff --git a/src/Components/Medicine/ManagePrescriptions.tsx b/src/Components/Medicine/ManagePrescriptions.tsx index a5ae50813a2..8409e721779 100644 --- a/src/Components/Medicine/ManagePrescriptions.tsx +++ b/src/Components/Medicine/ManagePrescriptions.tsx @@ -10,7 +10,15 @@ export default function ManagePrescriptions() { const { goBack } = useAppHistory(); return ( - + + + Print + + } + >
{ const prescriptionList = [ ...(data?.results ?? []), - ...(showDiscontinued ? discontinuedPrescriptions.data?.results ?? [] : []), + ...(showDiscontinued + ? (discontinuedPrescriptions.data?.results ?? []) + : []), ]; const { activityTimelineBounds, prescriptions } = useMemo( @@ -90,25 +92,37 @@ const MedicineAdministrationSheet = ({ readonly, is_prn }: Props) => { options={ !readonly && !!data?.results && ( - + <> + + + + + {t("edit_prescriptions")} + + {t("edit")} + + refetch()} + /> + - - - {t("edit_prescriptions")} - - {t("edit")} + + Print - refetch()} - /> - + ) } /> diff --git a/src/Components/Medicine/PrintPreview.tsx b/src/Components/Medicine/PrintPreview.tsx new file mode 100644 index 00000000000..09bad44d630 --- /dev/null +++ b/src/Components/Medicine/PrintPreview.tsx @@ -0,0 +1,271 @@ +import { useTranslation } from "react-i18next"; +import PrintPreview from "../../CAREUI/misc/PrintPreview"; +import { useSlugs } from "../../Common/hooks/useSlug"; +import routes from "../../Redux/api"; +import useQuery from "../../Utils/request/useQuery"; +import { + classNames, + formatDate, + formatDateTime, + formatName, + patientAgeInYears, +} from "../../Utils/utils"; +import MedicineRoutes from "./routes"; +import { Prescription } from "./models"; +import useConfig from "../../Common/hooks/useConfig"; +import { ReactNode } from "react"; + +export default function PrescriptionsPrintPreview() { + const { main_logo } = useConfig(); + const { t } = useTranslation(); + const [patientId, consultationId] = useSlugs("patient", "consultation"); + + const patientQuery = useQuery(routes.getPatient, { + pathParams: { id: patientId }, + }); + + const encounterQuery = useQuery(routes.getConsultation, { + pathParams: { id: consultationId }, + }); + + const prescriptionsQuery = useQuery(MedicineRoutes.listPrescriptions, { + pathParams: { consultation: consultationId }, + query: { discontinued: false, limit: 100 }, + }); + + const patient = patientQuery.data; + const encounter = encounterQuery.data; + + const items = prescriptionsQuery.data?.results; + const normalPrescriptions = items?.filter((p) => p.dosage_type !== "PRN"); + const prnPrescriptions = items?.filter((p) => p.dosage_type === "PRN"); + + return ( + +
+

{encounter?.facility_name}

+ care logo +
+
+ + {patient && ( + <> + {patient.name} -{" "} + {t(`GENDER__${patient.gender}`)},{" "} + {patientAgeInYears(patient).toString()}yrs + + )} + + + {encounter?.patient_no} + + + + {formatDate(encounter?.encounter_date)} + + + {encounter?.current_bed?.bed_object.location_object?.name} + {" - "} + {encounter?.current_bed?.bed_object.name} + + + + {patient?.allergies ?? "None"} + +
+ + + + +
+

+ Sign of the Consulting Doctor +

+ + {encounter?.treating_physician_object && + formatName(encounter?.treating_physician_object)} + +

+ Generated on: {formatDateTime(new Date())} +

+

+ This is a computer generated prescription. It shall be issued to the + patient only after the concerned doctor has verified the content and + authorized the same by affixing signature. +

+
+
+ ); +} + +const PatientDetail = ({ + name, + children, + className, +}: { + name: string; + children?: ReactNode; + className?: string; +}) => { + return ( +
+
{name}:
+ {children != null ? ( + {children} + ) : ( +
+ )} +
+ ); +}; + +const PrescriptionsTable = ({ + items, + prn, +}: { + items?: Prescription[]; + prn?: boolean; +}) => { + if (!items) { + return ( +
+ ); + } + + if (!items.length) { + return; + } + + return ( + + + + + + + + {/* */} + + + + + {items.map((item) => ( + + ))} + +
+ {prn && "PRN"} Prescriptions +
MedicineDosageDirections{prn ? "Indicator" : "Freq."}Notes / Instructions
+ ); +}; + +const PrescriptionEntry = ({ obj }: { obj: Prescription }) => { + const { t } = useTranslation(); + const medicine = obj.medicine_object; + + return ( + + +

+ + {medicine?.name ?? obj.medicine_old} + {" "} +

+ {medicine?.type === "brand" && ( + +

+ Generic:{" "} + + {medicine.generic ?? "--"} + +

+

+ Brand:{" "} + + {medicine.company ?? "--"} + +

+
+ )} + + + {obj.dosage_type === "TITRATED" &&

Titrated

} +

+ {obj.base_dosage}{" "} + {obj.target_dosage != null && `→ ${obj.target_dosage}`}{" "} +

+ {obj.max_dosage && ( +

+ Max. {obj.max_dosage} in + 24hrs +

+ )} + {obj.min_hours_between_doses && ( +

+ Min.{" "} + + {obj.min_hours_between_doses}hrs + {" "} + b/w doses +

+ )} + + + {obj.route && ( +

+ Route: + + {t(`PRESCRIPTION_ROUTE_${obj.route}`)} + +

+ )} + {obj.frequency && ( +

+ Freq: + + {t(`PRESCRIPTION_FREQUENCY_${obj.frequency}`)} + +

+ )} + {obj.days && ( +

+ Days: + {obj.days} day(s) +

+ )} + {obj.indicator && ( +

+ Indicator: + {obj.indicator} +

+ )} + + + {obj.notes} + {obj.instruction_on_titration && ( +

+ Titration instructions:{" "} + {obj.instruction_on_titration} +

+ )} + + + ); +}; diff --git a/src/Locale/en/Common.json b/src/Locale/en/Common.json index 0bd26305b86..707f8f74a0b 100644 --- a/src/Locale/en/Common.json +++ b/src/Locale/en/Common.json @@ -177,5 +177,8 @@ "caution": "Caution", "feed_optimal_experience_for_phones": "For optimal viewing experience, consider rotating your device.", "feed_optimal_experience_for_apple_phones": "For optimal viewing experience, consider rotating your device. Ensure auto-rotate is enabled in your device settings.", - "action_irreversible": "This action is irreversible" + "action_irreversible": "This action is irreversible", + "GENDER__1": "Male", + "GENDER__2": "Female", + "GENDER__3": "Non-binary" } \ No newline at end of file diff --git a/src/Locale/en/Consultation.json b/src/Locale/en/Consultation.json index 6e3846fb983..a76afe72410 100644 --- a/src/Locale/en/Consultation.json +++ b/src/Locale/en/Consultation.json @@ -38,6 +38,12 @@ "no_changes": "No changes", "no_treating_physicians_available": "This facility does not have any home facility doctors. Please contact your admin.", "encounter_suggestion_edit_disallowed": "Not allowed to switch to this option in edit consultation", + "encounter_suggestion__A": "Admission", + "encounter_suggestion__DC": "Domiciliary Care", + "encounter_suggestion__OP": "Out-patient visit", + "encounter_suggestion__DD": "Consultation", + "encounter_suggestion__HI": "Consultation", + "encounter_suggestion__R": "Consultation", "encounter_date_field_label__A": "Date & Time of Admission to the Facility", "encounter_date_field_label__DC": "Date & Time of Domiciliary Care commencement", "encounter_date_field_label__OP": "Date & Time of Out-patient visit", diff --git a/src/Locale/en/Medicine.json b/src/Locale/en/Medicine.json index d559ef2fdbf..80726d83fb2 100644 --- a/src/Locale/en/Medicine.json +++ b/src/Locale/en/Medicine.json @@ -47,7 +47,7 @@ "PRESCRIPTION_ROUTE_IM": "IM", "PRESCRIPTION_ROUTE_SC": "S/C", "PRESCRIPTION_ROUTE_INHALATION": "Inhalation", - "PRESCRIPTION_ROUTE_NASOGASTRIC": "Nasogastric/Gastrostomy tube", + "PRESCRIPTION_ROUTE_NASOGASTRIC": "Nasogastric / Gastrostomy tube", "PRESCRIPTION_ROUTE_INTRATHECAL": "intrathecal injection", "PRESCRIPTION_ROUTE_TRANSDERMAL": "Transdermal", "PRESCRIPTION_ROUTE_RECTAL": "Rectal", @@ -61,4 +61,4 @@ "PRESCRIPTION_FREQUENCY_Q4H": "4th hourly", "PRESCRIPTION_FREQUENCY_QOD": "Alternate day", "PRESCRIPTION_FREQUENCY_QWK": "Once a week" -} +} \ No newline at end of file diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index 8b75e3f147f..2484acca0fd 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -10,6 +10,7 @@ import { ConsultationDetails } from "../../Components/Facility/ConsultationDetai import TreatmentSummary from "../../Components/Facility/TreatmentSummary"; import ConsultationDoctorNotes from "../../Components/Facility/ConsultationDoctorNotes"; import PatientConsentRecords from "../../Components/Patient/PatientConsentRecords"; +import PrescriptionsPrintPreview from "../../Components/Medicine/PrintPreview"; export default { "/facility/:facilityId/patient/:patientId/consultation": ({ @@ -48,6 +49,8 @@ export default { ), "/facility/:facilityId/patient/:patientId/consultation/:consultationId/prescriptions": (path: any) => , + "/facility/:facilityId/patient/:patientId/consultation/:consultationId/prescriptions/print": + () => , "/facility/:facilityId/patient/:patientId/consultation/:id/investigation": ({ facilityId, patientId, From 12b57b9e7e6d0c00f8d069f4ef2e42b5bdf198cd Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Thu, 8 Aug 2024 18:08:55 +0530 Subject: [PATCH 12/17] Made Scribe work with Symptoms, Investigations, Diagnosis and Prescriptions (#8183) * Fixed Symptoms and Diagnosis * fixes * fixed bugs * fixed times, measured at, and rounds type * fix translation issue * minor enhancement * fixed rounds type * disable scribe * init * Added support for Investigations, and Prescriptions * undo config * Update src/Components/Scribe/formDetails.ts * Fix bug related to diagnosis update * cleanup * more cleanup --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> --- .../InvestigationBuilder.tsx | 89 +++---- .../ConsultationDiagnosisBuilder.tsx | 7 +- src/Components/Patient/DailyRounds.tsx | 140 +++++++++- src/Components/Scribe/Scribe.tsx | 140 +++++++--- src/Components/Scribe/formDetails.ts | 239 ++++++++++++++++-- 5 files changed, 510 insertions(+), 105 deletions(-) diff --git a/src/Components/Common/prescription-builder/InvestigationBuilder.tsx b/src/Components/Common/prescription-builder/InvestigationBuilder.tsx index ef99a7f1acc..ce7c579ee22 100644 --- a/src/Components/Common/prescription-builder/InvestigationBuilder.tsx +++ b/src/Components/Common/prescription-builder/InvestigationBuilder.tsx @@ -29,12 +29,28 @@ export interface InvestigationBuilderProps { setInvestigations: React.Dispatch>; } -export default function InvestigationBuilder( - props: InvestigationBuilderProps, -) { - const { investigations, setInvestigations } = props; - const [investigationsList, setInvestigationsList] = useState([]); - const [activeIdx, setActiveIdx] = useState(null); +export const loadInvestigations = async () => { + const fetchInvestigations = async () => { + const { data } = await request(routes.listInvestigations); + return ( + data?.results.map( + (investigation) => + `${investigation.name} -- ${humanizeStrings( + investigation.groups.map((group) => ` ( ${group.name} ) `), + )}`, + ) ?? [] + ); + }; + + const fetchInvestigationGroups = async () => { + const { data } = await request(routes.listInvestigationGroups); + return data?.results.map((group) => `${group.name} (GROUP)`) ?? []; + }; + + const invs = await fetchInvestigations(); + const groups = await fetchInvestigationGroups(); + + let additionalStrings: string[] = []; const additionalInvestigations = [ ["Vitals", ["Temp", "Blood Pressure", "Respiratory Rate", "Pulse Rate"]], [ @@ -51,57 +67,42 @@ export default function InvestigationBuilder( ], ], ]; + additionalInvestigations.forEach((investigation) => { + additionalStrings.push((investigation[0] as string) + " (GROUP)"); + additionalStrings = [ + ...additionalStrings, + ...(investigation[1] as string[]).map( + (i: any) => i + " -- ( " + investigation[0] + " )", + ), + ]; + }); + + return [...groups, ...invs, ...additionalStrings]; +}; + +export default function InvestigationBuilder( + props: InvestigationBuilderProps, +) { + const { investigations, setInvestigations } = props; + const [investigationsList, setInvestigationsList] = useState([]); + const [activeIdx, setActiveIdx] = useState(null); const setItem = (object: InvestigationType, i: number) => { setInvestigations( - investigations.map((investigation, index) => + investigations?.map((investigation, index) => index === i ? object : investigation, ), ); }; useEffect(() => { - loadInvestigations(); + const load = async () => setInvestigationsList(await loadInvestigations()); + load(); }, []); - const loadInvestigations = async () => { - const invs = await fetchInvestigations(); - const groups = await fetchInvestigationGroups(); - - let additionalStrings: string[] = []; - additionalInvestigations.forEach((investigation) => { - additionalStrings.push((investigation[0] as string) + " (GROUP)"); - additionalStrings = [ - ...additionalStrings, - ...(investigation[1] as string[]).map( - (i: any) => i + " -- ( " + investigation[0] + " )", - ), - ]; - }); - - setInvestigationsList([...groups, ...invs, ...additionalStrings]); - }; - - const fetchInvestigations = async () => { - const { data } = await request(routes.listInvestigations); - return ( - data?.results.map( - (investigation) => - `${investigation.name} -- ${humanizeStrings( - investigation.groups.map((group) => ` ( ${group.name} ) `), - )}`, - ) ?? [] - ); - }; - - const fetchInvestigationGroups = async () => { - const { data } = await request(routes.listInvestigationGroups); - return data?.results.map((group) => `${group.name} (GROUP)`) ?? []; - }; - return (
- {investigations.map((investigation, i) => { + {investigations?.map((investigation, i) => { const setFrequency = (frequency: string) => { setItem( { diff --git a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx index 0391748c929..b6495143f5d 100644 --- a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx +++ b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import useSlug from "../../../Common/hooks/useSlug"; import { ConsultationDiagnosis, @@ -83,6 +83,11 @@ interface EditDiagnosesProps { export const EditDiagnosesBuilder = (props: EditDiagnosesProps) => { const consultation = useSlug("consultation"); const [diagnoses, setDiagnoses] = useState(props.value); + + useEffect(() => { + setDiagnoses(props.value); + }, [props.value]); + return (
diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index d451513c9bc..d960636d522 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -29,7 +29,7 @@ import RadioFormField from "../Form/FormFields/RadioFormField"; import request from "../../Utils/request/request"; import routes from "../../Redux/api"; import { Scribe } from "../Scribe/Scribe"; -import { DAILY_ROUND_FORM_SCRIBE_DATA } from "../Scribe/formDetails"; +import { SCRIBE_FORMS } from "../Scribe/formDetails"; import { DailyRoundsModel } from "./models"; import InvestigationBuilder from "../Common/prescription-builder/InvestigationBuilder"; import { FieldErrorText } from "../Form/FormFields/FormField"; @@ -45,6 +45,9 @@ import { EncounterSymptomsBuilder } from "../Symptoms/SymptomsBuilder"; import { FieldLabel } from "../Form/FormFields/FormField"; import useAuthUser from "../../Common/hooks/useAuthUser"; import CheckBoxFormField from "../Form/FormFields/CheckBoxFormField"; +import SymptomsApi from "../Symptoms/api"; +import DiagnosesRoutes from "../Diagnosis/routes"; +import MedicineRoutes from "../Medicine/routes"; import { scrollTo } from "../../Utils/utils"; const Loading = lazy(() => import("../Common/Loading")); @@ -54,6 +57,8 @@ export const DailyRounds = (props: any) => { const authUser = useAuthUser(); const { goBack } = useAppHistory(); const { facilityId, patientId, consultationId, id } = props; + const [symptomsSeed, setSymptomsSeed] = useState(1); + const [prescriptionSeed, setPrescriptionSeed] = useState(1); const initForm: any = { physical_examination_info: "", @@ -478,11 +483,129 @@ export const DailyRounds = (props: any) => { >
{ + form={SCRIBE_FORMS.daily_round} + onFormUpdate={async (fields) => { + // Symptoms + let rounds_type = fields.rounds_type || state.form.rounds_type; + if (fields.additional_symptoms) { + for (const symptom of fields.additional_symptoms) { + const { res } = await request(SymptomsApi.add, { + pathParams: { consultationId }, + body: { + ...symptom, + }, + }); + if (res?.ok) setSymptomsSeed((s) => s + 1); + } + } + + // ICD11 Diagnosis + if (fields.icd11_diagnosis) { + for (const diagnosis of fields.icd11_diagnosis) { + // Fetch available diagnoses + + const { res: icdRes, data: icdData } = await request( + routes.listICD11Diagnosis, + { + query: { query: diagnosis.diagnosis }, + }, + ); + + if (!icdRes?.ok) { + error({ + text: "Failed to fetch ICD11 Diagnosis", + }); + continue; + } + + const availableDiagnosis = icdData?.[0]?.id; + + if (!availableDiagnosis) { + error({ + text: "Could not find the requested diagnosis. Please enter manually.", + }); + continue; + } + + const { res, data } = await request( + DiagnosesRoutes.createConsultationDiagnosis, + { + pathParams: { consultation: consultationId }, + body: { + ...diagnosis, + diagnosis: availableDiagnosis, + }, + }, + ); + + if (res?.ok && data) + setDiagnoses((diagnoses) => [...(diagnoses || []), data]); + } + } + + // Prescriptions + if (fields.prescriptions || fields.prn_prescriptions) { + const combined_prescriptions = [ + ...(fields.prescriptions || []), + ...(fields.prn_prescriptions || []), + ]; + for (const prescription of combined_prescriptions) { + // fetch medicine + const { res: medicineRes, data: medicineData } = await request( + routes.listMedibaseMedicines, + { + query: { query: prescription.medicine }, + }, + ); + + if (!medicineRes?.ok) { + error({ + text: "Failed to fetch medicine", + }); + continue; + } + + const availableMedicine = medicineData?.[0]?.id; + + if (!availableMedicine) { + error({ + text: "Could not find the requested medicine. Please enter manually.", + }); + continue; + } + + const { res } = await request( + MedicineRoutes.createPrescription, + { + pathParams: { consultation: consultationId }, + body: { + ...prescription, + medicine: availableMedicine, + }, + }, + ); + + if (res?.ok) setPrescriptionSeed((s) => s + 1); + } + } + + if ( + Object.keys(fields).some((f) => + [ + "investigations", + "icd11_diagnosis", + "additional_symptoms", + "prescriptions", + "prn_prescriptions", + ].includes(f), + ) + ) { + rounds_type = "DOCTORS_LOG"; + } + dispatch({ type: "set_form", - form: { ...state.form, ...fields }, + form: { ...state.form, ...fields, rounds_type }, }); fields.action !== undefined && setPreviousAction(fields.action); fields.review_interval !== undefined && @@ -536,6 +659,7 @@ export const DailyRounds = (props: any) => {
Symptoms { handleFormFieldChange({ name: "symptoms_dirty", @@ -593,7 +717,11 @@ export const DailyRounds = (props: any) => { <>

Vitals

- + { discontinued={ showDiscontinuedPrescriptions ? undefined : false } + key={prescriptionSeed} actions={["discontinue"]} />
@@ -783,6 +912,7 @@ export const DailyRounds = (props: any) => { showDiscontinuedPrescriptions ? undefined : false } actions={["discontinue"]} + key={prescriptionSeed} />
diff --git a/src/Components/Scribe/Scribe.tsx b/src/Components/Scribe/Scribe.tsx index 16b9056a795..0b05f55ebdf 100644 --- a/src/Components/Scribe/Scribe.tsx +++ b/src/Components/Scribe/Scribe.tsx @@ -21,8 +21,15 @@ export interface Field { description: string; type: string; example: string; - default: string; + default: any; options?: readonly FieldOption[]; + validator: (value: any) => boolean; +} + +export interface ScribeForm { + id: string; + name: string; + fields: () => Promise | Field[]; } export type ScribeModel = { @@ -45,7 +52,8 @@ export type ScribeModel = { }; interface ScribeProps { - fields: Field[]; + form: ScribeForm; + existingData?: { [key: string]: any }; onFormUpdate: (fields: any) => void; } @@ -54,7 +62,7 @@ const SCRIBE_FILE_TYPES = { SCRIBE: 1, }; -export const Scribe: React.FC = ({ fields, onFormUpdate }) => { +export const Scribe: React.FC = ({ form, onFormUpdate }) => { const { enable_scribe } = useConfig(); const [open, setOpen] = useState(false); const [_progress, setProgress] = useState(0); @@ -71,6 +79,21 @@ export const Scribe: React.FC = ({ fields, onFormUpdate }) => { const [updatedTranscript, setUpdatedTranscript] = useState(""); const [scribeID, setScribeID] = useState(""); const stageRef = useRef(stage); + const [fields, setFields] = useState([]); + + useEffect(() => { + const loadFields = async () => { + const fields = await form.fields(); + setFields( + fields.map((f) => ({ + ...f, + validate: undefined, + default: JSON.stringify(f.default), + })), + ); + }; + loadFields(); + }, [form]); useEffect(() => { if (stageRef.current === "cancelled") { @@ -312,8 +335,20 @@ export const Scribe: React.FC = ({ fields, onFormUpdate }) => { setProgress(100); const parsedFormData = JSON.parse(updatedFieldsResponse ?? "{}"); if (stageRef.current === "cancelled") return; - setFormFields(parsedFormData); - onFormUpdate(parsedFormData); + + // run type validations + const validated = Object.entries(parsedFormData) + .filter(([k, v]) => { + const f = fields.find((f) => f.id === k); + if (!f) return false; + if (v === f.default) return false; + //if (f.validator) return f.validator(f.type === "number" ? Number(v) : v); + return true; + }) + .map(([k, v]) => ({ [k]: v })) + .reduce((acc, curr) => ({ ...acc, ...curr }), {}); + setFormFields(validated as any); + onFormUpdate(validated); setStage("final-review"); } catch (error) { setErrors(["Error retrieving form data"]); @@ -373,35 +408,76 @@ export const Scribe: React.FC = ({ fields, onFormUpdate }) => { stageRef.current = "cancelled"; }; - const processFormField = ( + function processFormField( fieldDetails: Field | undefined, - formFields: { [key: string]: string | string[] | number }, + formFields: { [key: string]: any }, field: string, - ) => { - if (fieldDetails?.options) { - // Check if the form field is an array (multiple selections allowed) - if (Array.isArray(formFields[field])) { - // Map each selected option ID to its corresponding text - return (formFields[field] as string[]) - .map((option) => { - const optionDetails = fieldDetails.options?.find( - (o) => o.id === option, - ); - return optionDetails?.text ?? option; // Use option text if found, otherwise fallback to option ID - }) - .join(", "); - } else { - // Single selection scenario, find the option that matches the field value - return ( - fieldDetails.options?.find((o) => o.id === formFields[field])?.text ?? - JSON.stringify(formFields[field]) - ); + ): React.ReactNode { + const value = formFields[field]; + if (!fieldDetails || !value) return value; + + const { options } = fieldDetails; + + const getHumanizedKey = (key: string): string => { + return key + .split("_") + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(" "); + }; + + const getOptionText = (value: string | number): string => { + if (!options) return value.toString(); + const option = options.find((opt) => opt.id === value); + return option ? option.text : value.toString(); + }; + + const renderPrimitive = (value: any): any => { + return options ? getOptionText(value) : value; + }; + + const renderArray = (values: any[]): React.ReactNode => { + return values.map((value) => renderPrimitive(value)).join(", "); + }; + + const renderObject = (obj: { [key: string]: any }): React.ReactNode => { + return ( +
+ {Object.keys(obj).map((key, keyIndex) => ( +
+ {getHumanizedKey(key)}: {renderPrimitive(obj[key])} +
+ ))} +
+ ); + }; + + const renderObjectArray = (objects: any[]): React.ReactNode => { + return ( +
+ {objects.map((obj, objIndex) => ( +
{renderObject(obj)}
+ ))} +
+ ); + }; + + if (Array.isArray(value)) { + if ( + value.length > 0 && + typeof value[0] === "object" && + !Array.isArray(value[0]) + ) { + return renderObjectArray(value); } - } else { - // If no options are available, return the field value in JSON string format - return JSON.stringify(formFields[field]); + return renderArray(value); } - }; + + if (typeof value === "object") { + return renderObject(value); + } + + return renderPrimitive(value); + } const renderContentBasedOnStage = () => { switch (stage) { @@ -599,13 +675,13 @@ export const Scribe: React.FC = ({ fields, onFormUpdate }) => {

{fieldDetails?.friendlyName}

-

+

{processFormField( fieldDetails, formFields, field, )} -

+
); })} diff --git a/src/Components/Scribe/formDetails.ts b/src/Components/Scribe/formDetails.ts index 74673adea70..1c5b0cf3b9c 100644 --- a/src/Components/Scribe/formDetails.ts +++ b/src/Components/Scribe/formDetails.ts @@ -5,19 +5,27 @@ import { RHYTHM_CHOICES, TELEMEDICINE_ACTIONS, } from "../../Common/constants"; +import { loadInvestigations } from "../Common/prescription-builder/InvestigationBuilder"; import { SYMPTOM_CHOICES } from "../Symptoms/types"; -import { Field } from "./Scribe"; +import { Field, ScribeForm } from "./Scribe"; -export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ +const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ { friendlyName: "Additional Symptoms", id: "additional_symptoms", - type: "number[]", - example: "[1,2,3]", - default: "[]", - description: - "A numeric array of option IDs to store symptoms of the patient.", + type: "{symptom: number, other_symptom?: string, onset_date: string, cure_date?: string}[]", + example: + "[{symptom: 1, onset_date: '2024-12-03'}, {symptom: 2, onset_date: '2024-12-03', cure_date: '2024-12-05'}, {symptom: 9, other_symptom: 'Other symptom', onset_date: '2024-12-03'}]", + default: [], + description: `An array of objects to store the patient's symptoms along with their date of onset and date of cure (if any). The symptom field should be an integer corresponding to the symptom's ID. The onset_date and cure_date fields should be date strings (e.g., '2022-01-01'). If no onset_date has been specified, use todays date which is '${new Date().toISOString().slice(0, 10)}'. If the symptom is ongoing, the cure_date field should not be included. If the user has 'Other Symptom', only then the other_symptom field should be included with a string value describing the symptom.`, options: SYMPTOM_CHOICES, + validator: (value) => { + if (!Array.isArray(value)) return false; + value.forEach((s) => { + if (!s.symptom || !s.onset_date) return false; + }); + return true; + }, }, { friendlyName: "Other Symptoms", @@ -26,6 +34,9 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ example: "", default: "", description: "Just leave it blank", + validator: () => { + return true; + }, }, { friendlyName: "Physical Examination Info", @@ -36,6 +47,9 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ default: "", description: "This field is designated for storing detailed findings from the physical examination of the patient. It should include all observable physical attributes, conditions, or symptoms noted during the examination. When processing a doctor's transcript, identify and extract descriptions that pertain directly to the patient's physical state, such as visible conditions, physical symptoms, or any abnormalities noted by touch, sight, or measurement. This can include, but is not limited to, descriptions of skin conditions, swellings, lacerations, posture, mobility issues, and any other physically observable traits.", + validator: (value) => { + return typeof value === "string"; + }, }, { friendlyName: "Other Details", @@ -46,6 +60,9 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ "Patient reports trouble sleeping and a decreased appetite. Additionally, the patient is allergic to penicillin and has a history of asthma.", description: "This field is for capturing any supplementary details about the patient that are mentioned in the doctor's transcript but do not directly pertain to the physical examination findings. This includes, but is not limited to, behavioral observations, medical history, patient complaints, lifestyle factors, allergies, or any other non-physical observations that are relevant to the patient's overall health and well-being. When processing a transcript, extract information that describes the patient's health, habits, or conditions in a broader sense than what is observed through physical examination alone.", + validator: (value) => { + return typeof value === "string"; + }, }, { friendlyName: "Patient Category", @@ -58,14 +75,20 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ id: category.id, text: category.text, })), + validator: (value) => { + return typeof value === "string"; + }, }, { friendlyName: "Actions", id: "actions", type: "null", example: "null", - default: "null", + default: null, description: "Leave blank.", + validator: (value) => { + return value === null; + }, }, { friendlyName: "Action", @@ -78,16 +101,18 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ id: action.text, text: action.desc, })), + validator: (value) => typeof value === "string", }, { friendlyName: "Review Interval", id: "review_interval", type: "number", - default: "0", + default: 0, example: "15", description: "An integer to represent the interval at which the patient's condition is reviewed.", options: REVIEW_AT_CHOICES, + validator: (value) => typeof value === "number", }, { friendlyName: "Admitted To", @@ -97,63 +122,75 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ example: "General Ward", description: "A string to store the department or ward where the patient is admitted.", + validator: (value) => typeof value === "string", }, { friendlyName: "bp", id: "bp", - default: "{ systolic: undefined, diastolic: undefined, mean: undefined }", - type: "{ systolic: number, diastolic: number, mean: number }", - example: "{ systolic: 120, diastolic: 80, mean: 100 }", + default: { systolic: null, diastolic: null, mean: null }, + type: "{ systolic?: number, diastolic?: number }", + example: "{ systolic: 120 }", description: - "An object to store the blood pressure of the patient. It contains two integers, systolic and diastolic. Output mean is calculated from these two.", + "An object to store the blood pressure of the patient. It may contain two integers, systolic and diastolic.", + validator: (value) => { + if (typeof value !== "object") return false; + if (value.systolic && typeof value.systolic !== "number") return false; + if (value.diastolic && typeof value.diastolic !== "number") return false; + return true; + }, }, { friendlyName: "Pulse", id: "pulse", type: "number", - default: "null", + default: null, example: "72", description: "An integer to store the pulse rate of the patient. It can be null if the pulse rate is not taken.", + validator: (value) => typeof value === "number", }, { friendlyName: "Respiratory Rate", id: "resp", type: "number", - default: "null", + default: null, example: "16", description: "An integer to store the respiratory rate of the patient. It can be null if the respiratory rate is not taken.", + validator: (value) => typeof value === "number", }, { friendlyName: "Temperature", id: "temperature", type: "number", - default: "null", + default: null, example: "98.6", description: "A float to store the temperature of the patient. It can be null if the temperature is not taken.", + validator: (value) => typeof value === "number", }, { friendlyName: "SPO2", id: "ventilator_spo2", type: "number", - default: "null", + default: null, example: "98", description: "An integer to store the SPO2 level of the patient. It can be null if the SPO2 level is not taken.", + validator: (value) => typeof value === "number", }, { friendlyName: "Rhythm", id: "rhythm", - type: "string", + type: "number", example: "5", - default: "0", + default: 0, description: "An option to store the rhythm of the patient.", options: RHYTHM_CHOICES.map((rhythm) => ({ id: rhythm.id, text: rhythm.desc ?? "", })), + validator: (value) => typeof value === "number", }, { friendlyName: "Rhythm Detail", @@ -163,6 +200,7 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ example: "Just minor irregularities.", description: "A string to store the details about the rhythm of the patient.", + validator: (value) => typeof value === "string", }, { friendlyName: "Level Of Consciousness", @@ -173,13 +211,168 @@ export const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ description: "An option to store the level of consciousness of the patient.", options: CONSCIOUSNESS_LEVEL, + validator: (value) => typeof value === "string", + }, + { + friendlyName: "Diagnosis", + id: "icd11_diagnosis", + type: "{diagnosis: string, verification_status: \"unconfirmed\" | \"provisional\" | \"differential\" | \"confirmed\", is_principal: boolean}[]", + default: [], + example: + "[{diagnosis: '4A42.0 Paediatric onset systemic sclerosis', verification_status: 'confirmed', is_principal: true}, {diagnosis: 2, verification_status: 'provisional', is_principal: false}]", + description: + "A list of objects to store the patient's diagnosis along with their verification status and whether it is the principal diagnosis. If not specifically said, set is_principal to false. NOTE: only one principal diagnosis can exist. The diagnosis field should be a string that may contain a corresponding diagnosis ID. The verification_status field should be a string with one of the following values: 'unconfirmed', 'provisional', 'differential', or 'confirmed'.", + validator: (value) => { + if (!Array.isArray(value)) return false; + value.forEach((d) => { + if (!d.diagnosis || !d.verification_status) return false; + }); + return true; + }, + }, + { + friendlyName: "Investigations", + id: "investigations", + type: `{ + type: string[], + repetitive: boolean, + time?: string, + frequency?: '15 min' | '30 min' | '1 hr' | '6 hrs' | '12 hrs' | '24 hrs' | '48 hrs', + notes?: string + }[]`, + default: [], + example: `[ + { + type: ["Haemotology (GROUP)"], + repetitive: false, + time: "2024-07-31T18:10", + notes: "Patient is allergic to penicillin." + }, + { + type: ["ECG", "X-Ray"], + repetitive: true, + frequency: "24 hrs", + notes: "Patient is going nuts" + } + ]`, + description: + "A list of objects to store the patient's investigations. The type field should be an array of strings corresponding to the names of the investigations provided in the options. The repetitive field should be a boolean value. The time field should be a string and only be filled if repetitive field is false. The frequency field should be a string with one of the following values: '15 min', '30 min', '1 hr', '6 hrs', '12 hrs', '24 hrs', or '48 hrs' and should be only filled if this is a repititive investigation. The time field should be of the example format if present - (2024-07-31T18:10). The notes field should be a string. If the type is not available in options, DO NOT MAKE IT.", + validator: (value) => { + if (!Array.isArray(value)) return false; + value.forEach((i) => { + if (!i.type || !i.repetitive) return false; + if (i.repetitive && !i.frequency) return false; + }); + return true; + }, + }, + { + friendlyName: "Prescriptions", + id: "prescriptions", + type: `{ + base_dosage: number + " " + ("mg" | "g" | "ml" | "drop(s)" | "ampule(s)" | "tsp" | "mcg" | "unit(s)"), + days: number, + dosage_type: "REGULAR" | "TITRATED", + frequency: "STAT" | "OD" | "HS" | "BD" | "TID" | "QID" | "Q4H" | "QOD" | "QWK", + medicine: string, + notes: string, + route: "ORAL" | "IV" | "IM" | "SC" | "INHALATION" | "NASOGASTRIC" | "INTRATHECAL" | "TRANSDERMAL" | "RECTAL" | "SUBLINGUAL", + instruction_on_titration: string, + target_dosage: number + " " + ("mg" | "g" | "ml" | "drop(s)" | "ampule(s)" | "tsp" | "mcg" | "unit(s)"), + }[]`, + default: [], + example: `[ + {base_dosage: "5 ampule(s)", days: 7, dosage_type: "REGULAR", frequency: "STAT", medicine: "DOLO", notes: "Give with water", route: "ORAL"}, + {base_dosage: "7 ml", days: 3, dosage_type: "TITRATED", frequency: "Q4H", medicine: "Albumin", route: "INHALATION", instruction_on_titration: "Example", target_dosage: "40 ml"}, + ]`, + description: `A list of objects to store the patient's prescriptions. The prescription can be regular or titrated. If titrated, the prescription should also include instruction_on_titration, and a target_dosage. NOTE: target_dosage should have the same unit as base_dosage. + The frequency should be any of the mentioned ones. They are short for: + STAT: Imediately, + OD: Once daily, + HS: Night Only, + BD: Twice Daily, + TID: 8th Hourly, + QID: 6th Hourly, + Q4H: 4th Hourly, + QOD: Alternate Day, + QWK: Once a Week + `, + validator: (value) => { + if (!Array.isArray(value)) return false; + return true; + }, + }, + { + friendlyName: "PRN Prescriptions", + id: "prn_prescriptions", + type: `{ + base_dosage: number + " " + ("mg" | "g" | "ml" | "drop(s)" | "ampule(s)" | "tsp" | "mcg" | "unit(s)"), + dosage_type: "PRN", + medicine: string, + notes: string, + route: "ORAL" | "IV" | "IM" | "SC" | "INHALATION" | "NASOGASTRIC" | "INTRATHECAL" | "TRANSDERMAL" | "RECTAL" | "SUBLINGUAL", + indicator: string, + min_hours_between_doses: number, + max_dosage: number + " " + ("mg" | "g" | "ml" | "drop(s)" | "ampule(s)" | "tsp" | "mcg" | "unit(s)"), + }[]`, + default: [], + example: `[ + {base_dosage: "3 drop(s)", dosage_type:"PRN", indicator: "If patient gets fever", max_dosage: "5 drops(s)", min_hours_between_doses: 12, route: "IV", medicine: "Glentona", notes: "Example"} + ]`, + description: "A list of objects to store the patient's PRN prescriptions.", + validator: (value) => { + if (!Array.isArray(value)) return false; + return true; + }, }, + /*{ + friendlyName: "Round Type", + id: "rounds_type", + type: "string", + default: "NORMAL", + example: "TELEMEDICINE", + description: "A string to store the type of round.", + options: [ + { id: "NORMAL", text: "Brief Update" }, + { id: "VENTILATOR", text: "Detailed Update" }, + { id: "DOCTORS_LOG", text: "Progress Note" }, + { id: "TELEMEDICINE", text: "Telemedicine" }, + ], + validator: (value) => typeof value === "string", + }, + { + friendlyName: "Measured At", + id: "taken_at", + type: "string", + default: "", + example: "2024-07-31T18:10", + description: + "A string to store the date and time at which the round was taken or measured. 'The round was taken yesterday/today' would amount to yesterday/today's date.", + validator: (value) => typeof value === "string", + }, +*/ ]; -export const SCRIBE_FORMS = [ - { +export const SCRIBE_FORMS: { [key: string]: ScribeForm } = { + daily_round: { id: "daily_round", name: "Daily Round", - fields: DAILY_ROUND_FORM_SCRIBE_DATA, + fields: async () => { + const investigations = await loadInvestigations(); + + return DAILY_ROUND_FORM_SCRIBE_DATA.map((field) => { + if (field.id === "investigations") { + return { + ...field, + options: investigations.map((investigation, i) => ({ + id: i, + text: investigation, + currentData: undefined, + })), + }; + } + return field; + }); + }, }, -]; +}; From 56dd9222a6e1ad65600be077c96acf5330dcff22 Mon Sep 17 00:00:00 2001 From: Ankur Prabhu <85862184+AnkurPrabhu@users.noreply.github.com> Date: Fri, 9 Aug 2024 10:48:52 +0530 Subject: [PATCH 13/17] Fix Notifications for Facility Cover Image Deletion (#7609) --- .../Facility/CoverImageEditModal.tsx | 69 +++++++++++-------- 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/src/Components/Facility/CoverImageEditModal.tsx b/src/Components/Facility/CoverImageEditModal.tsx index 319ae60fd1b..dc8e0fcd6d7 100644 --- a/src/Components/Facility/CoverImageEditModal.tsx +++ b/src/Components/Facility/CoverImageEditModal.tsx @@ -28,6 +28,22 @@ interface Props { facility: FacilityModel; } +const VideoConstraints = { + user: { + width: 1280, + height: 720, + facingMode: "user", + }, + environment: { + width: 1280, + height: 720, + facingMode: { exact: "environment" }, + }, +} as const; + +type IVideoConstraint = + (typeof VideoConstraints)[keyof typeof VideoConstraints]; + const CoverImageEditModal = ({ open, onClose, @@ -35,31 +51,26 @@ const CoverImageEditModal = ({ onDelete, facility, }: Props) => { - const [isUploading, setIsUploading] = useState(false); - const [selectedFile, setSelectedFile] = useState(); + const [isProcessing, setIsProcessing] = useState(false); + const [selectedFile, setSelectedFile] = useState(); const [preview, setPreview] = useState(); const [isCameraOpen, setIsCameraOpen] = useState(false); const webRef = useRef(null); const [previewImage, setPreviewImage] = useState(null); const [isCaptureImgBeingUploaded, setIsCaptureImgBeingUploaded] = useState(false); - const FACING_MODE_USER = "user"; - const FACING_MODE_ENVIRONMENT = { exact: "environment" }; - const [facingMode, setFacingMode] = useState(FACING_MODE_USER); - const videoConstraints = { - width: 1280, - height: 720, - facingMode: "user", - }; + const [constraint, setConstraint] = useState( + VideoConstraints.user, + ); const { width } = useWindowDimensions(); const LaptopScreenBreakpoint = 640; const isLaptopScreen = width >= LaptopScreenBreakpoint; const { t } = useTranslation(); const handleSwitchCamera = useCallback(() => { - setFacingMode((prevState: any) => - prevState === FACING_MODE_USER - ? FACING_MODE_ENVIRONMENT - : FACING_MODE_USER, + setConstraint((prev) => + prev.facingMode === "user" + ? VideoConstraints.environment + : VideoConstraints.user, ); }, []); @@ -106,7 +117,7 @@ const CoverImageEditModal = ({ const formData = new FormData(); formData.append("cover_image", selectedFile); const url = `/api/v1/facility/${facility.id}/cover_image/`; - setIsUploading(true); + setIsProcessing(true); uploadFile( url, @@ -123,7 +134,7 @@ const CoverImageEditModal = ({ Notification.Error({ msg: "Something went wrong!", }); - setIsUploading(false); + setIsProcessing(false); } }, null, @@ -131,26 +142,28 @@ const CoverImageEditModal = ({ Notification.Error({ msg: "Network Failure. Please check your internet connectivity.", }); - setIsUploading(false); + setIsProcessing(false); }, ); await sleep(1000); - setIsUploading(false); + setIsProcessing(false); setIsCaptureImgBeingUploaded(false); onSave && onSave(); closeModal(); }; const handleDelete = async () => { + setIsProcessing(true); const { res } = await request(routes.deleteFacilityCoverImage, { pathParams: { id: facility.id! }, }); if (res?.ok) { Success({ msg: "Cover image deleted" }); - onDelete?.(); - closeModal(); } + setIsProcessing(false); + onDelete?.(); + closeModal(); }; const hasImage = !!(preview || facility.read_cover_image_url); @@ -277,13 +290,13 @@ const CoverImageEditModal = ({ closeModal(); dragProps.setFileDropError(""); }} - disabled={isUploading} + disabled={isProcessing} /> {facility.read_cover_image_url && ( {t("delete")} @@ -291,15 +304,15 @@ const CoverImageEditModal = ({ - {isUploading ? ( + {isProcessing ? ( ) : ( )} - {isUploading ? `${t("uploading")}...` : `${t("save")}`} + {isProcessing ? `${t("uploading")}...` : `${t("save")}`}
@@ -320,7 +333,7 @@ const CoverImageEditModal = ({ screenshotFormat="image/jpeg" width={1280} ref={webRef} - videoConstraints={{ ...videoConstraints, facingMode }} + videoConstraints={constraint} /> ) : ( @@ -365,7 +378,7 @@ const CoverImageEditModal = ({ setPreviewImage(null); }} className="my-2 w-full" - disabled={isUploading} + disabled={isProcessing} > {t("retake")} @@ -430,7 +443,7 @@ const CoverImageEditModal = ({ > {t("retake")} - + {isCaptureImgBeingUploaded ? ( <> Date: Fri, 9 Aug 2024 10:49:08 +0530 Subject: [PATCH 14/17] Added sorting for events (#8270) --- src/Common/constants.tsx | 7 +++++++ .../ConsultationDetails/Events/EventsList.tsx | 19 ++++++++++++++++++- src/Locale/en/SortOptions.json | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 4a490f34e78..2bfecf94395 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -201,6 +201,13 @@ export const PATIENT_SORT_OPTIONS: SortOption[] = [ { isAscending: false, value: "-name" }, ]; +export const EVENTS_SORT_OPTIONS: SortOption[] = [ + { isAscending: false, value: "-created_date" }, + { isAscending: true, value: "created_date" }, + { isAscending: false, value: "-taken_at" }, + { isAscending: true, value: "taken_at" }, +]; + export const DISCHARGED_PATIENT_SORT_OPTIONS: SortOption[] = [ { isAscending: false, value: "-created_date" }, { isAscending: true, value: "created_date" }, diff --git a/src/Components/Facility/ConsultationDetails/Events/EventsList.tsx b/src/Components/Facility/ConsultationDetails/Events/EventsList.tsx index f759559c1a1..d77b3d9a8c2 100644 --- a/src/Components/Facility/ConsultationDetails/Events/EventsList.tsx +++ b/src/Components/Facility/ConsultationDetails/Events/EventsList.tsx @@ -7,15 +7,32 @@ import LoadingLogUpdateCard from "../../Consultations/DailyRounds/LoadingCard"; import GenericEvent from "./GenericEvent"; import { getEventIcon } from "./iconMap"; import { EventGeneric } from "./types"; +import SortDropdownMenu from "../../../Common/SortDropdown"; +import { EVENTS_SORT_OPTIONS } from "../../../../Common/constants"; +import { QueryParams } from "../../../../Utils/request/types"; +import { useState } from "react"; export default function EventsList() { const [consultationId] = useSlugs("consultation"); const { t } = useTranslation(); + const [query, setQuery] = useState(); return ( - + {() => ( <> +
+ +
+
diff --git a/src/Locale/en/SortOptions.json b/src/Locale/en/SortOptions.json index 11986b97663..29a8d1e8f4d 100644 --- a/src/Locale/en/SortOptions.json +++ b/src/Locale/en/SortOptions.json @@ -9,6 +9,8 @@ "facility__name,-last_consultation__current_bed__bed__name": "Bed No. N-1", "-review_time": "Latest review date first", "review_time": "Oldest review date first", + "taken_at": "Oldest taken date first", + "-taken_at": "Latest taken date first", "name": "Patient name A-Z", "-name": "Patient name Z-A", "bed__name": "Bed No. 1-N", From 18e988866eeac6e968ff4a8ba5e2814f5c8b3b17 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Fri, 9 Aug 2024 15:53:56 +0530 Subject: [PATCH 15/17] =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Fixes=20issue=20w?= =?UTF-8?q?ith=20treating=20physician=20field=20being=20disabled=20when=20?= =?UTF-8?q?a=20search=20text=20entered=20yields=20no=20results;=20?= =?UTF-8?q?=F0=9F=9B=A0=EF=B8=8F=20Migrate=20`UserAutocompleteFormField`?= =?UTF-8?q?=20to=20use=20`useQuery`=20(#8274)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add tests to replicate the issue * refactor name formatting to use utility fn * Upgrade UserAutocompleteFormField to use useQuery and have dedicated subcomponents based on linked facility or users api query * remove unused import * fix types * update cypress * fix issue with mergeQuery options and cleanup * fix cypress syntax error * add id for autocomplete input * update test * fix cypress * skip explicit clearing * remove test --- src/Components/ABDM/ABDMFacilityRecords.tsx | 11 +- src/Components/ABDM/ABDMRecordsTab.tsx | 4 +- src/Components/Assets/AssetManage.tsx | 5 +- .../Common/RelativeDateUserMention.tsx | 4 +- .../Common/UserAutocompleteFormField.tsx | 210 +++++++++++------- src/Components/Facility/ConsultationForm.tsx | 22 +- .../Facility/DoctorVideoSlideover.tsx | 13 +- src/Components/Facility/FacilityUsers.tsx | 4 +- src/Components/Facility/PatientNoteCard.tsx | 10 +- src/Components/Notifications/NoticeBoard.tsx | 4 +- src/Components/Patient/PatientHome.tsx | 20 +- src/Components/Patient/PatientInfoCard.tsx | 3 +- src/Components/Resource/CommentSection.tsx | 5 +- src/Components/Resource/ResourceBoard.tsx | 5 +- src/Components/Resource/ResourceDetails.tsx | 11 +- .../Resource/ResourceDetailsUpdate.tsx | 4 +- src/Components/Shifting/BadgesList.tsx | 9 +- src/Components/Shifting/CommentsSection.tsx | 5 +- src/Components/Shifting/ListFilter.tsx | 6 +- src/Components/Shifting/ShiftDetails.tsx | 11 +- .../Shifting/ShiftDetailsUpdate.tsx | 12 +- src/Components/Shifting/ShiftingBoard.tsx | 5 +- src/Components/Users/ManageUsers.tsx | 11 +- src/Redux/actions.tsx | 14 -- 24 files changed, 217 insertions(+), 191 deletions(-) diff --git a/src/Components/ABDM/ABDMFacilityRecords.tsx b/src/Components/ABDM/ABDMFacilityRecords.tsx index eadf39b8db7..cd21d269536 100644 --- a/src/Components/ABDM/ABDMFacilityRecords.tsx +++ b/src/Components/ABDM/ABDMFacilityRecords.tsx @@ -86,8 +86,8 @@ export default function ABDMFacilityRecords({ facilityId }: IProps) { consent.expiry, ) < new Date() ? "EXPIRED" - : consent.consent_artefacts?.[0]?.status ?? - consent.status} + : (consent.consent_artefacts?.[0]?.status ?? + consent.status)} @@ -102,13 +102,6 @@ export default function ABDMFacilityRecords({ facilityId }: IProps) { : "-"} - {/* - {`${consent.requester?.first_name} ${consent.requester?.last_name}`.trim()} -

- ({consent.requester.username}) -

- */} - {formatDateTime( consent.consent_artefacts?.[0]?.from_time ?? diff --git a/src/Components/ABDM/ABDMRecordsTab.tsx b/src/Components/ABDM/ABDMRecordsTab.tsx index d828335cd0d..37ae1a629db 100644 --- a/src/Components/ABDM/ABDMRecordsTab.tsx +++ b/src/Components/ABDM/ABDMRecordsTab.tsx @@ -5,7 +5,7 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; import ButtonV2 from "../Common/components/ButtonV2"; import * as Notification from "../../Utils/Notifications.js"; import Loading from "../Common/Loading"; -import { classNames } from "../../Utils/utils"; +import { classNames, formatName } from "../../Utils/utils"; import { Link } from "raviger"; import routes from "../../Redux/api"; import request from "../../Utils/request/request"; @@ -75,7 +75,7 @@ function ConsentRequestCard({ consent }: IConsentRequestCardProps) { }
- {consent.requester.first_name} {consent.requester.last_name} + {formatName(consent.requester)}
diff --git a/src/Components/Assets/AssetManage.tsx b/src/Components/Assets/AssetManage.tsx index a9bb6eb78ea..469d1556fb5 100644 --- a/src/Components/Assets/AssetManage.tsx +++ b/src/Components/Assets/AssetManage.tsx @@ -10,7 +10,7 @@ import Pagination from "../Common/Pagination"; import { navigate } from "raviger"; import QRCode from "qrcode.react"; import AssetWarrantyCard from "./AssetWarrantyCard"; -import { formatDate, formatDateTime } from "../../Utils/utils"; +import { formatDate, formatDateTime, formatName } from "../../Utils/utils"; import Chip from "../../CAREUI/display/Chip"; import CareIcon from "../../CAREUI/icons/CareIcon"; import ButtonV2 from "../Common/components/ButtonV2"; @@ -148,8 +148,7 @@ const AssetManage = (props: AssetManageProps) => { - {transaction.performed_by.first_name}{" "} - {transaction.performed_by.last_name} + {formatName(transaction.performed_by)} diff --git a/src/Components/Common/RelativeDateUserMention.tsx b/src/Components/Common/RelativeDateUserMention.tsx index 70eadc5b7ed..541c38c3537 100644 --- a/src/Components/Common/RelativeDateUserMention.tsx +++ b/src/Components/Common/RelativeDateUserMention.tsx @@ -1,5 +1,5 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; -import { formatDateTime, relativeDate } from "../../Utils/utils"; +import { formatDateTime, formatName, relativeDate } from "../../Utils/utils"; import { PerformedByModel } from "../HCX/misc"; function RelativeDateUserMention(props: { @@ -28,7 +28,7 @@ function RelativeDateUserMention(props: { }`} >
-

{`${props.user.first_name} ${props.user.last_name}`}

+

{formatName(props.user)}

{`@${props.user.username}`}

{props.user.user_type}

diff --git a/src/Components/Common/UserAutocompleteFormField.tsx b/src/Components/Common/UserAutocompleteFormField.tsx index 442eb0ae4fe..3ff6a3ae7c2 100644 --- a/src/Components/Common/UserAutocompleteFormField.tsx +++ b/src/Components/Common/UserAutocompleteFormField.tsx @@ -1,112 +1,158 @@ -import { useAsyncOptions } from "../../Common/hooks/useAsyncOptions"; -import { getFacilityUsers, getUserList } from "../../Redux/actions"; import { Autocomplete } from "../Form/FormFields/Autocomplete"; import FormField from "../Form/FormFields/FormField"; import { FormFieldBaseProps, useFormFieldPropsResolver, } from "../Form/FormFields/Utils"; -import { UserModel } from "../Users/models"; -import { isUserOnline } from "../../Utils/utils"; +import { + classNames, + formatName, + isUserOnline, + mergeQueryOptions, +} from "../../Utils/utils"; import { UserRole } from "../../Common/constants"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; +import useQuery from "../../Utils/request/useQuery"; +import routes from "../../Redux/api"; +import { UserBareMinimum } from "../Users/models"; -type Props = FormFieldBaseProps & { +type BaseProps = FormFieldBaseProps & { placeholder?: string; - facilityId?: string; - homeFacility?: string; userType?: UserRole; - showActiveStatus?: boolean; noResultsError?: string; }; -export default function UserAutocompleteFormField(props: Props) { +type LinkedFacilitySearchProps = BaseProps & { + facilityId: string; + homeFacility?: undefined; +}; + +type UserSearchProps = BaseProps & { + facilityId?: undefined; + homeFacility?: string; +}; + +export default function UserAutocomplete(props: UserSearchProps) { const field = useFormFieldPropsResolver(props); - const { fetchOptions, isLoading, options } = useAsyncOptions( - "id", - { queryResponseExtractor: (data) => data.results }, - ); + const [query, setQuery] = useState(""); + const [disabled, setDisabled] = useState(false); - let search_filter: { - limit: number; - offset: number; - home_facility?: string; - user_type?: string; - search_text?: string; - } = { limit: 50, offset: 0 }; + const { data, loading } = useQuery(routes.userList, { + query: { + home_facility: props.homeFacility, + user_type: props.userType, + search_text: query, + limit: 50, + offset: 0, + }, + }); - if (props.showActiveStatus && props.userType) { - search_filter = { ...search_filter, user_type: props.userType }; - } + useEffect(() => { + if ( + loading || + query || + !field.required || + !props.noResultsError || + !data?.results + ) { + return; + } - if (props.homeFacility) { - search_filter = { ...search_filter, home_facility: props.homeFacility }; - } + if (data.results.length === 0) { + setDisabled(true); + field.handleChange(undefined as unknown as UserBareMinimum); + } + }, [loading, query, field.required, data?.results, props.noResultsError]); - const getStatusIcon = (option: UserModel) => { - if (!props.showActiveStatus) return null; + return ( + + obj.username, + )} + optionLabel={formatName} + optionIcon={userOnlineDot} + optionDescription={(option) => + `${option.user_type} - ${option.username}` + } + optionValue={(option) => option} + onQuery={setQuery} + isLoading={loading} + /> + + ); +} - return ( -
- - - -
- ); - }; +export const LinkedFacilityUsers = (props: LinkedFacilitySearchProps) => { + const field = useFormFieldPropsResolver(props); - const items = options(field.value && [field.value]); + const [query, setQuery] = useState(""); - useEffect(() => { - if (props.required && !isLoading && !items.length && props.noResultsError) { - field.handleChange(undefined as unknown as UserModel); - } - }, [isLoading, items, props.required]); + const { data, loading } = useQuery(routes.getFacilityUsers, { + pathParams: { facility_id: props.facilityId }, + query: { + user_type: props.userType, + search_text: query, + limit: 50, + offset: 0, + }, + }); const noResultError = - (props.required && !isLoading && !items.length && props.noResultsError) || + (!query && + !loading && + field.required && + !data?.results?.length && + props.noResultsError) || undefined; + useEffect(() => { + if (noResultError) { + field.handleChange(undefined as unknown as UserBareMinimum); + } + }, [noResultError]); + return ( -
- `${option.user_type}`} - optionValue={(option) => option} - onQuery={(query) => - fetchOptions( - props.facilityId - ? getFacilityUsers(props.facilityId, { - ...search_filter, - search_text: query, - }) - : getUserList({ ...search_filter, search_text: query }), - ) - } - isLoading={isLoading} - /> -
+ obj.username, + )} + optionLabel={formatName} + optionIcon={userOnlineDot} + optionDescription={(option) => + `${option.user_type} - ${option.username}` + } + optionValue={(option) => option} + onQuery={setQuery} + isLoading={loading} + />
); -} - -const getUserFullName = (user: UserModel) => { - const personName = user.first_name + " " + user.last_name; - return personName.trim().length > 0 ? personName : user.username || ""; }; + +const userOnlineDot = (user: UserBareMinimum) => ( +
+); diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index 40d9741d712..c9339ce685b 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -33,8 +33,8 @@ import PatientCategorySelect from "../Patient/PatientCategorySelect"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; -import UserAutocompleteFormField from "../Common/UserAutocompleteFormField"; -import { UserModel } from "../Users/models"; +import UserAutocomplete from "../Common/UserAutocompleteFormField"; +import { UserBareMinimum } from "../Users/models"; import { navigate } from "raviger"; import useAppHistory from "../../Common/hooks/useAppHistory"; @@ -90,7 +90,7 @@ type FormDetails = { referred_by_external?: string; transferred_from_location?: string; treating_physician: string; - treating_physician_object: UserModel | null; + treating_physician_object: UserBareMinimum | null; create_diagnoses: CreateDiagnosis[]; diagnoses: ConsultationDiagnosis[]; symptoms: EncounterSymptom[]; @@ -107,7 +107,7 @@ type FormDetails = { is_telemedicine: BooleanStrings; action?: number; assigned_to: string; - assigned_to_object: UserModel | null; + assigned_to_object: UserBareMinimum | null; special_instruction: string; review_interval: number; weight: string; @@ -386,8 +386,8 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { admitted: data.admitted ? String(data.admitted) : "false", admitted_to: data.admitted_to ? data.admitted_to : "", category: data.category - ? PATIENT_CATEGORIES.find((i) => i.text === data.category)?.id ?? - "" + ? (PATIENT_CATEGORIES.find((i) => i.text === data.category)?.id ?? + "") : "", patient_no: data.patient_no ?? "", OPconsultation: data.consultation_notes, @@ -782,7 +782,9 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { } }; - const handleDoctorSelect = (event: FieldChangeEvent) => { + const handleDoctorSelect = ( + event: FieldChangeEvent, + ) => { if (event.value?.id) { dispatch({ type: "set_form", @@ -1430,7 +1432,7 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { className="col-span-6" ref={fieldRef["treating_physician"]} > - { state.form.treating_physician_object ?? undefined } onChange={handleDoctorSelect} - showActiveStatus userType={"Doctor"} homeFacility={facilityId} error={state.errors.treating_physician} @@ -1483,8 +1484,7 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { className="col-span-6 flex-[2]" ref={fieldRef["assigned_to"]} > -
- - {user.first_name} {user.last_name} - + {formatName(user)} - {`${user.first_name} ${user.last_name}`} + {formatName(user)}
diff --git a/src/Components/Facility/PatientNoteCard.tsx b/src/Components/Facility/PatientNoteCard.tsx index 7f00700ff47..7d2a8c6eb70 100644 --- a/src/Components/Facility/PatientNoteCard.tsx +++ b/src/Components/Facility/PatientNoteCard.tsx @@ -1,4 +1,9 @@ -import { relativeDate, formatDateTime, classNames } from "../../Utils/utils"; +import { + relativeDate, + formatDateTime, + classNames, + formatName, +} from "../../Utils/utils"; import { USER_TYPES_MAP } from "../../Common/constants"; import { PatientNotesEditModel, PatientNotesModel } from "./models"; import ButtonV2 from "../Common/components/ButtonV2"; @@ -78,8 +83,7 @@ const PatientNoteCard = ({
- {note.created_by_object?.first_name || "Unknown"}{" "} - {note.created_by_object?.last_name} + {formatName(note.created_by_object)} {note.user_type && ( diff --git a/src/Components/Notifications/NoticeBoard.tsx b/src/Components/Notifications/NoticeBoard.tsx index db285eb9d6a..98c4daba2c4 100644 --- a/src/Components/Notifications/NoticeBoard.tsx +++ b/src/Components/Notifications/NoticeBoard.tsx @@ -1,6 +1,6 @@ import Page from "../Common/components/Page"; import Loading from "../Common/Loading"; -import { formatDateTime } from "../../Utils/utils"; +import { formatDateTime, formatName } from "../../Utils/utils"; import { useTranslation } from "react-i18next"; import CareIcon from "../../CAREUI/icons/CareIcon"; import useQuery from "../../Utils/request/useQuery"; @@ -25,7 +25,7 @@ export const NoticeBoard = () => {
{item.message}
- {`${item.caused_by.first_name} ${item.caused_by.last_name}`} -{" "} + {formatName(item.caused_by)} -{" "} {item.caused_by.user_type} diff --git a/src/Components/Patient/PatientHome.tsx b/src/Components/Patient/PatientHome.tsx index 1bee5a3b245..9482dc103dd 100644 --- a/src/Components/Patient/PatientHome.tsx +++ b/src/Components/Patient/PatientHome.tsx @@ -18,6 +18,7 @@ import { classNames, formatDate, formatDateTime, + formatName, formatPatientAge, isAntenatal, isPostPartum, @@ -30,7 +31,7 @@ import { useTranslation } from "react-i18next"; import CircularProgress from "../Common/components/CircularProgress"; import Page from "../Common/components/Page"; import ConfirmDialog from "../Common/ConfirmDialog"; -import UserAutocompleteFormField from "../Common/UserAutocompleteFormField"; +import UserAutocomplete from "../Common/UserAutocompleteFormField"; import dayjs from "../../Utils/dayjs"; import { triggerGoal } from "../../Integrations/Plausible"; import useAuthUser from "../../Common/hooks/useAuthUser"; @@ -275,14 +276,9 @@ export const PatientHome = (props: any) => {

Assigned Doctor: - { - patientData?.last_consultation?.assigned_to_object - .first_name - } - { - patientData?.last_consultation?.assigned_to_object - .last_name - } + {formatName( + patientData.last_consultation.assigned_to_object, + )} {patientData?.last_consultation?.assigned_to_object .alt_phone_number && ( @@ -300,8 +296,7 @@ export const PatientHome = (props: any) => {

Assigned Volunteer: - {patientData.assigned_to_object.first_name} - {patientData.assigned_to_object.last_name} + {formatName(patientData.assigned_to_object)}

)} @@ -1395,8 +1390,7 @@ export const PatientHome = (props: any) => { onClose={() => setOpenAssignVolunteerDialog(false)} description={
- {consultation?.treating_physician_object - ? `${consultation?.treating_physician_object.first_name} ${consultation?.treating_physician_object.last_name}` + ? formatName(consultation.treating_physician_object) : consultation?.deprecated_verified_by} - {created_by_object?.first_name || "Unknown"}{" "} - {created_by_object?.last_name} + {formatName(created_by_object)}
diff --git a/src/Components/Resource/ResourceBoard.tsx b/src/Components/Resource/ResourceBoard.tsx index 352942aff83..c80d11bf361 100644 --- a/src/Components/Resource/ResourceBoard.tsx +++ b/src/Components/Resource/ResourceBoard.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from "react"; import { downloadResourceRequests } from "../../Redux/actions"; import { navigate } from "raviger"; -import { classNames } from "../../Utils/utils"; +import { classNames, formatName } from "../../Utils/utils"; import { useDrag, useDrop } from "react-dnd"; import { formatDateTime } from "../../Utils/utils"; import { ExportButton } from "../Common/Export"; @@ -126,8 +126,7 @@ const ResourceCard = ({ resource }: any) => { >
- {resource.assigned_to_object.first_name}{" "} - {resource.assigned_to_object.last_name} -{" "} + {formatName(resource.assigned_to_object)} -{" "} {resource.assigned_to_object.user_type}
diff --git a/src/Components/Resource/ResourceDetails.tsx b/src/Components/Resource/ResourceDetails.tsx index c719556d561..4c01bdc4b9c 100644 --- a/src/Components/Resource/ResourceDetails.tsx +++ b/src/Components/Resource/ResourceDetails.tsx @@ -1,5 +1,5 @@ import { useState, lazy } from "react"; -import { classNames, formatDateTime } from "../../Utils/utils"; +import { classNames, formatDateTime, formatName } from "../../Utils/utils"; import { navigate } from "raviger"; import * as Notification from "../../Utils/Notifications.js"; import CommentSection from "./CommentSection"; @@ -241,8 +241,7 @@ export default function ResourceDetails(props: { id: string }) {

- Assigned to: {data.assigned_to_object.first_name}{" "} - {data.assigned_to_object.last_name} -{" "} + Assigned to: {formatName(data.assigned_to_object)} -{" "} {data.assigned_to_object.user_type}

@@ -359,8 +358,7 @@ export default function ResourceDetails(props: { id: string }) {
- {data?.created_by_object?.first_name}{" "} - {data?.created_by_object?.last_name} + {formatName(data.created_by_object)}
{data.created_date && formatDateTime(data.created_date)} @@ -373,8 +371,7 @@ export default function ResourceDetails(props: { id: string }) {
- {data?.last_edited_by_object?.first_name}{" "} - {data?.last_edited_by_object?.last_name} + {formatName(data.last_edited_by_object)}
{data.modified_date && formatDateTime(data.modified_date)} diff --git a/src/Components/Resource/ResourceDetailsUpdate.tsx b/src/Components/Resource/ResourceDetailsUpdate.tsx index 89d4d5a15ea..178ab596148 100644 --- a/src/Components/Resource/ResourceDetailsUpdate.tsx +++ b/src/Components/Resource/ResourceDetailsUpdate.tsx @@ -13,7 +13,7 @@ import RadioFormField from "../Form/FormFields/RadioFormField"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; -import UserAutocompleteFormField from "../Common/UserAutocompleteFormField"; +import UserAutocomplete from "../Common/UserAutocompleteFormField"; import useAppHistory from "../../Common/hooks/useAppHistory"; import useQuery from "../../Utils/request/useQuery.js"; import routes from "../../Redux/api.js"; @@ -209,7 +209,7 @@ export const ResourceDetailsUpdate = (props: resourceProps) => { {assignedUserLoading ? ( ) : ( - - {created_by_object?.first_name || t("unknown")}{" "} - {created_by_object?.last_name} + {formatName(created_by_object)}
diff --git a/src/Components/Shifting/ListFilter.tsx b/src/Components/Shifting/ListFilter.tsx index af98f524ed2..12bb17db09c 100644 --- a/src/Components/Shifting/ListFilter.tsx +++ b/src/Components/Shifting/ListFilter.tsx @@ -21,7 +21,7 @@ import useConfig from "../../Common/hooks/useConfig"; import useMergeState from "../../Common/hooks/useMergeState"; import { useTranslation } from "react-i18next"; -import UserAutocompleteFormField from "../Common/UserAutocompleteFormField"; +import UserAutocomplete from "../Common/UserAutocompleteFormField"; import { dateQueryString, parsePhoneNumber } from "../../Utils/utils"; import dayjs from "dayjs"; import useQuery from "../../Utils/request/useQuery"; @@ -165,7 +165,7 @@ export default function ListFilter(props: any) { patient_phone_number: patient_phone_number === "+91" ? "" - : parsePhoneNumber(patient_phone_number) ?? "", + : (parsePhoneNumber(patient_phone_number) ?? ""), created_date_before: dateQueryString(created_date_before), created_date_after: dateQueryString(created_date_after), modified_date_before: dateQueryString(modified_date_before), @@ -268,7 +268,7 @@ export default function ListFilter(props: any) { {isAssignedLoading ? ( ) : ( -

- {t("assigned_to")}: {data?.assigned_to_object.first_name}{" "} - {data.assigned_to_object.last_name} -{" "} - {data.assigned_to_object.user_type} + {t("assigned_to")}: {formatName(data.assigned_to_object)}{" "} + - {data.assigned_to_object.user_type}

diff --git a/src/Components/Shifting/ShiftDetailsUpdate.tsx b/src/Components/Shifting/ShiftDetailsUpdate.tsx index 35e77066ef4..37adecaedcd 100644 --- a/src/Components/Shifting/ShiftDetailsUpdate.tsx +++ b/src/Components/Shifting/ShiftDetailsUpdate.tsx @@ -33,8 +33,8 @@ import CircularProgress from "../Common/components/CircularProgress.js"; import Card from "../../CAREUI/display/Card"; import RadioFormField from "../Form/FormFields/RadioFormField.js"; import Page from "../Common/components/Page.js"; -import UserAutocompleteFormField from "../Common/UserAutocompleteFormField.js"; -import { UserModel } from "../Users/models.js"; +import { LinkedFacilityUsers } from "../Common/UserAutocompleteFormField.js"; +import { UserBareMinimum } from "../Users/models.js"; import useQuery from "../../Utils/request/useQuery.js"; import routes from "../../Redux/api.js"; import { IShift } from "./models.js"; @@ -57,7 +57,7 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { const [qParams, _] = useQueryParams(); const [isLoading, setIsLoading] = useState(true); - const [assignedUser, SetAssignedUser] = useState(); + const [assignedUser, SetAssignedUser] = useState(); const [consultationData, setConsultationData] = useState( {} as ConsultationModel, @@ -184,7 +184,9 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { return !isInvalidForm; }; - const handleAssignedUserSelect = (event: FieldChangeEvent) => { + const handleAssignedUserSelect = ( + event: FieldChangeEvent, + ) => { const user = event.value; const form = { ...state.form }; form["assigned_to"] = user?.id; @@ -357,7 +359,7 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { (assignedUserLoading ? ( ) : ( - { >
- {shift.assigned_to_object.first_name}{" "} - {shift.assigned_to_object.last_name} -{" "} + {formatName(shift.assigned_to_object)} + {" - "} {shift.assigned_to_object.user_type}
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx index 19bafebd115..1acd9ac2a3c 100644 --- a/src/Components/Users/ManageUsers.tsx +++ b/src/Components/Users/ManageUsers.tsx @@ -14,7 +14,12 @@ import routes from "../../Redux/api.js"; import * as Notification from "../../Utils/Notifications.js"; import request from "../../Utils/request/request.js"; import useQuery from "../../Utils/request/useQuery.js"; -import { classNames, isUserOnline, relativeTime } from "../../Utils/utils"; +import { + classNames, + formatName, + isUserOnline, + relativeTime, +} from "../../Utils/utils"; import { FacilitySelect } from "../Common/FacilitySelect"; import Pagination from "../Common/Pagination"; import UserDetails from "../Common/UserDetails"; @@ -178,7 +183,7 @@ export default function ManageUsers() { setUserData({ show: true, username: user.username, - name: `${user.first_name} ${user.last_name}`, + name: formatName(user), }); }; @@ -238,7 +243,7 @@ export default function ManageUsers() { id="name" className="mt-2 flex items-center gap-3 text-2xl font-bold capitalize" > - {`${user.first_name} ${user.last_name}`} + {formatName(user)} {user.last_login && cur_online ? (
{ - return fireRequest("userList", [], params, null, key); -}; - -export const getFacilityUsers = (id: string, params?: object) => { - return fireRequest( - "getFacilityUsers", - [], - { ...params }, - { facility_id: id }, - ); -}; - // asset bed export const listAssetBeds = (params: object, altKey?: string) => fireRequest("listAssetBeds", [], params, {}, altKey); From 57304aa7bc51d98562a478be3e4027881eea92da Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Fri, 9 Aug 2024 15:54:12 +0530 Subject: [PATCH 16/17] Fixed date time format for edge cases (#8280) --- .../Facility/ConsultationDetails/Events/GenericEvent.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx b/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx index ca9c13c599b..04ab5657149 100644 --- a/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx +++ b/src/Components/Facility/ConsultationDetails/Events/GenericEvent.tsx @@ -32,7 +32,10 @@ const formatValue = (value: unknown, key?: string): ReactNode => { return trimmed; } - if (new Date(trimmed).toString() !== "Invalid Date") { + const dateTimeRegex = + /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?)?$/; + + if (trimmed.match(dateTimeRegex)) { return new Date(trimmed).toLocaleString(); } From e1184537c86f650f20cf42ed61c20fcd464f57af Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Mon, 12 Aug 2024 13:24:35 +0530 Subject: [PATCH 17/17] Replace old LOC options in Neuro table (#8291) --- .../Consultations/NeurologicalTables.tsx | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Components/Facility/Consultations/NeurologicalTables.tsx b/src/Components/Facility/Consultations/NeurologicalTables.tsx index aac558ca8b0..993f0e6cb9c 100644 --- a/src/Components/Facility/Consultations/NeurologicalTables.tsx +++ b/src/Components/Facility/Consultations/NeurologicalTables.tsx @@ -93,13 +93,17 @@ export const NeurologicalTable = (props: any) => { const [currentPage, setCurrentPage] = useState(1); const [totalCount, setTotalCount] = useState(0); + // To be removed in favour of proper fix upcoming in https://github.com/coronasafe/care_fe/pull/8119/files#diff-3f2dc697ea8b52c1b3b887c76623edb0a4e6ace175573dfbd3a7476ffee979a9L96-L103 const LOC_OPTIONS = [ - { id: 0, value: "Unknown" }, + { id: 20, value: "Unresponsive" }, + { id: 15, value: "Responds to Pain" }, + { id: 10, value: "Responds to Voice" }, { id: 5, value: "Alert" }, - { id: 10, value: "Drowsy" }, - { id: 15, value: "Stuporous" }, - { id: 20, value: "Comatose" }, - { id: 25, value: "Cannot Be Assessed" }, + { id: 25, value: "Agitated or Confused" }, + { + id: 30, + value: "Onset of Agitation and Confusion", + }, ]; const REACTION_OPTIONS = [ @@ -294,7 +298,7 @@ export const NeurologicalTable = (props: any) => { {locData.map((x: any, i: any) => (
{x.date}