From 901f619562694d602dfb6e574fb81bd7d0e2d8f7 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Thu, 11 Jan 2024 19:32:24 +0530 Subject: [PATCH 01/11] Fixes buggy prefetch condition for local body and district autocompletes (#7006) * Fixes #7005; buggy prefetch condition for local body and district autocompletes * fix not found --- src/Components/Common/DistrictAutocompleteFormField.tsx | 4 ++-- src/Components/Common/LocalBodyAutocompleteFormField.tsx | 4 ++-- src/Utils/request/types.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Components/Common/DistrictAutocompleteFormField.tsx b/src/Components/Common/DistrictAutocompleteFormField.tsx index dbee6c9f08d..25fef151f2a 100644 --- a/src/Components/Common/DistrictAutocompleteFormField.tsx +++ b/src/Components/Common/DistrictAutocompleteFormField.tsx @@ -11,8 +11,8 @@ type Props = FormFieldBaseProps & { export default function DistrictAutocompleteFormField(props: Props) { const { data, loading } = useQuery(routes.getDistrictByState, { - pathParams: { id: props.state?.toString() ?? "" }, - prefetch: props.state !== undefined, + pathParams: { id: props.state! }, + prefetch: !!props.state, }); return ( diff --git a/src/Components/Common/LocalBodyAutocompleteFormField.tsx b/src/Components/Common/LocalBodyAutocompleteFormField.tsx index 32023fa559f..8d0821cb43c 100644 --- a/src/Components/Common/LocalBodyAutocompleteFormField.tsx +++ b/src/Components/Common/LocalBodyAutocompleteFormField.tsx @@ -11,8 +11,8 @@ type Props = FormFieldBaseProps & { export default function LocalBodyAutocompleteFormField(props: Props) { const { data, loading } = useQuery(routes.getLocalbodyByDistrict, { - pathParams: { id: props.district?.toString() ?? "" }, - prefetch: props.district !== undefined, + pathParams: { id: props.district! }, + prefetch: !!props.district, }); return ( diff --git a/src/Utils/request/types.ts b/src/Utils/request/types.ts index e22d37f3090..aed066ac81c 100644 --- a/src/Utils/request/types.ts +++ b/src/Utils/request/types.ts @@ -30,7 +30,7 @@ export interface RequestResult { export interface RequestOptions { query?: QueryParams; body?: TBody; - pathParams?: Record; + pathParams?: Record; onResponse?: (res: RequestResult) => void; silent?: boolean; reattempts?: number; From f62ea5dc636d29f6892e5540370c9502841523a8 Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Thu, 11 Jan 2024 19:33:16 +0530 Subject: [PATCH 02/11] Refactor discharge_reason data type to Integer (#6807) * Refactor discharge_reason data type to Integer * Update discharge_reason to new_discharge_reason * Revert API target in vite.config.ts * Fix discharge reason and discharge notes validation --- src/Common/constants.tsx | 8 +-- .../ConsultationUpdatesTab.tsx | 21 ++++--- src/Components/Facility/ConsultationForm.tsx | 11 ++-- src/Components/Facility/DischargeModal.tsx | 55 +++++++++++-------- src/Components/Facility/models.tsx | 2 +- src/Components/Patient/ManagePatients.tsx | 15 ++--- src/Components/Patient/PatientFilter.tsx | 16 +++--- src/Components/Patient/PatientHome.tsx | 9 ++- src/Components/Patient/PatientInfoCard.tsx | 10 ++-- .../Shifting/ShiftDetailsUpdate.tsx | 5 +- 10 files changed, 92 insertions(+), 60 deletions(-) diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 647c77ecf57..55a35bef9ab 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -294,10 +294,10 @@ export const SYMPTOM_CHOICES = [ ]; export const DISCHARGE_REASONS = [ - { id: "REC", text: "Recovered" }, - { id: "EXP", text: "Expired" }, - { id: "REF", text: "Referred" }, - { id: "LAMA", text: "LAMA" }, + { id: 1, text: "Recovered" }, + { id: 2, text: "Referred" }, + { id: 3, text: "Expired" }, + { id: 4, text: "LAMA" }, ]; export const CONSCIOUSNESS_LEVEL = [ diff --git a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx index 5e5b574dd07..4158d7f6609 100644 --- a/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx +++ b/src/Components/Facility/ConsultationDetails/ConsultationUpdatesTab.tsx @@ -176,7 +176,8 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { {props.consultationData.discharge_date && (
i.text == "Recovered")?.id && "lg:col-span-2" }`} > @@ -190,11 +191,13 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => { {DISCHARGE_REASONS.find( (d) => - d.id === props.consultationData.discharge_reason + d.id === props.consultationData.new_discharge_reason )?.text ?? "--"}
- {props.consultationData.discharge_reason === "REF" && ( + {props.consultationData.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Referred") + ?.id && (
Referred Facility {" - "} @@ -204,7 +207,9 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
)} - {props.consultationData.discharge_reason === "REC" && ( + {props.consultationData.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Recovered") + ?.id && (
Discharge Date {" - "} @@ -239,7 +244,9 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
)} - {props.consultationData.discharge_reason === "EXP" && ( + {props.consultationData.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired") + ?.id && (
Date of Death {" - "} @@ -266,8 +273,8 @@ export const ConsultationUpdatesTab = (props: ConsultationTabProps) => {
)} - {["REF", "LAMA"].includes( - props.consultationData.discharge_reason ?? "" + {[2, 4].includes( + props.consultationData.new_discharge_reason ?? 0 ) && (
diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index 63e18645e13..a493d354be2 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -3,6 +3,7 @@ import * as Notification from "../../Utils/Notifications.js"; import { BedModel, FacilityModel } from "./models"; import { CONSULTATION_SUGGESTION, + DISCHARGE_REASONS, ConsultationSuggestionValue, PATIENT_CATEGORIES, REVIEW_AT_CHOICES, @@ -121,7 +122,7 @@ type FormDetails = { weight: string; height: string; bed: BedModel | null; - discharge_reason: string; + new_discharge_reason: number | null; cause_of_death: string; death_datetime: string; death_confirmed_doctor: string; @@ -171,7 +172,7 @@ const initForm: FormDetails = { weight: "", height: "", bed: null, - discharge_reason: "", + new_discharge_reason: null, cause_of_death: "", death_datetime: "", death_confirmed_doctor: "", @@ -402,7 +403,7 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { weight: res.data.weight ? res.data.weight : "", height: res.data.height ? res.data.height : "", bed: res.data?.current_bed?.bed_object || null, - discharge_reason: res.data?.discharge_reason || "", + new_discharge_reason: res.data?.new_discharge_reason || null, cause_of_death: res.data?.discharge_notes || "", death_datetime: res.data?.death_datetime || "", death_confirmed_doctor: res.data?.death_confirmed_doctor || "", @@ -653,7 +654,9 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { const dischargeResponse = await dispatchAction( dischargePatient( { - discharge_reason: "EXP", + new_discharge_reason: DISCHARGE_REASONS.find( + (i) => i.text === "Expired" + )?.id, discharge_notes: cause_of_death, death_datetime: death_datetime, death_confirmed_doctor: death_confirmed_doctor, diff --git a/src/Components/Facility/DischargeModal.tsx b/src/Components/Facility/DischargeModal.tsx index a15af4d3883..892be2e916b 100644 --- a/src/Components/Facility/DischargeModal.tsx +++ b/src/Components/Facility/DischargeModal.tsx @@ -26,7 +26,7 @@ import { FacilityModel } from "./models"; import dayjs from "../../Utils/dayjs"; interface PreDischargeFormInterface { - discharge_reason: string; + new_discharge_reason: number | null; discharge_notes: string; discharge_date?: string; death_datetime?: string; @@ -40,7 +40,7 @@ interface IProps { onClose: () => void; consultationData: ConsultationModel; afterSubmit?: () => void; - discharge_reason?: string; + new_discharge_reason?: number | null; discharge_notes?: string; discharge_date?: string; death_datetime?: string; @@ -51,7 +51,7 @@ const DischargeModal = ({ onClose, consultationData, afterSubmit, - discharge_reason = "", + new_discharge_reason = null, discharge_notes = "", discharge_date = dayjs().format("YYYY-MM-DDTHH:mm"), death_datetime = dayjs().format("YYYY-MM-DDTHH:mm"), @@ -60,7 +60,7 @@ const DischargeModal = ({ const dispatch: any = useDispatch(); const [preDischargeForm, setPreDischargeForm] = useState({ - discharge_reason, + new_discharge_reason, discharge_notes, discharge_date, death_datetime, @@ -110,17 +110,18 @@ const DischargeModal = ({ const handlePatientDischarge = async (value: boolean) => { setIsSendingDischargeApi(true); - if (!preDischargeForm.discharge_reason) { + if (!preDischargeForm.new_discharge_reason) { setErrors({ ...errors, - discharge_reason: "Please select a reason for discharge", + new_discharge_reason: "Please select a reason for discharge", }); setIsSendingDischargeApi(false); return; } if ( - preDischargeForm.discharge_reason == "EXP" && + preDischargeForm.new_discharge_reason == + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id && !preDischargeForm.discharge_notes.trim() ) { setErrors({ @@ -196,20 +197,21 @@ const DischargeModal = ({ label="Reason" name="discharge_reason" id="discharge_reason" - value={preDischargeForm.discharge_reason} - disabled={!!discharge_reason} + value={preDischargeForm.new_discharge_reason} + disabled={!!new_discharge_reason} options={DISCHARGE_REASONS} optionValue={({ id }) => id} optionLabel={({ text }) => text} onChange={(e) => setPreDischargeForm((prev) => ({ ...prev, - discharge_reason: e.value, + new_discharge_reason: e.value, })) } - error={errors?.discharge_reason} + error={errors?.new_discharge_reason} /> - {preDischargeForm.discharge_reason === "REF" && ( + {preDischargeForm.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Referred")?.id && ( <> Referred to )} i.text == "Expired")?.id + } label={ { - EXP: "Cause of death", - REC: "Discharged Advice", - }[preDischargeForm.discharge_reason] ?? "Notes" + "3": "Cause of death", + "1": "Discharged Advice", + }[preDischargeForm.new_discharge_reason ?? 0] ?? "Notes" } name="discharge_notes" value={preDischargeForm.discharge_notes} @@ -246,19 +251,22 @@ const DischargeModal = ({ /> i.text == "Expired")?.id ? "death_datetime" : "discharge_date" } label={ - preDischargeForm.discharge_reason === "EXP" + preDischargeForm.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id ? "Date of Death" : "Date and Time of Discharge" } type="datetime-local" value={ preDischargeForm[ - preDischargeForm.discharge_reason === "EXP" + preDischargeForm.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id ? "death_datetime" : "discharge_date" ] @@ -277,13 +285,15 @@ const DischargeModal = ({ )} max={dayjs().format("YYYY-MM-DDTHH:mm")} error={ - preDischargeForm.discharge_reason === "EXP" + preDischargeForm.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id ? errors?.death_datetime : errors?.discharge_date } /> - {preDischargeForm.discharge_reason === "REC" && ( + {preDischargeForm.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Recovered")?.id && ( <>
Discharge Prescription Medications @@ -295,7 +305,8 @@ const DischargeModal = ({
)} - {preDischargeForm.discharge_reason === "EXP" && ( + {preDischargeForm.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id && ( { }; const tabValue = - qParams.last_consultation_discharge_reason || qParams.is_active === "False" + qParams.last_consultation__new_discharge_reason || + qParams.is_active === "False" ? 1 : 0; @@ -163,7 +164,7 @@ export const PatientManager = () => { name: qParams.name || undefined, patient_no: qParams.patient_no || undefined, is_active: - !qParams.last_consultation_discharge_reason && + !qParams.last_consultation__new_discharge_reason && (qParams.is_active || "True"), disease_status: qParams.disease_status || undefined, phone_number: qParams.phone_number @@ -204,8 +205,8 @@ export const PatientManager = () => { qParams.last_consultation_discharge_date_after || undefined, last_consultation_admitted_bed_type_list: qParams.last_consultation_admitted_bed_type_list || undefined, - last_consultation_discharge_reason: - qParams.last_consultation_discharge_reason || undefined, + last_consultation__new_discharge_reason: + qParams.last_consultation__new_discharge_reason || undefined, last_consultation_current_bed__location: qParams.last_consultation_current_bed__location || undefined, srf_id: qParams.srf_id || undefined, @@ -352,7 +353,7 @@ export const PatientManager = () => { qParams.age_max, qParams.age_min, qParams.last_consultation_admitted_bed_type_list, - qParams.last_consultation_discharge_reason, + qParams.last_consultation__new_discharge_reason, qParams.last_consultation_current_bed__location, qParams.facility, qParams.facility_type, @@ -1022,10 +1023,10 @@ export const PatientManager = () => { }, value( "Discharge Reason", - "last_consultation_discharge_reason", + "last_consultation__new_discharge_reason", parseOptionId( DISCHARGE_REASONS, - qParams.last_consultation_discharge_reason + qParams.last_consultation__new_discharge_reason ) || "" ), ]} diff --git a/src/Components/Patient/PatientFilter.tsx b/src/Components/Patient/PatientFilter.tsx index 481d2dcc67b..4488aa755e1 100644 --- a/src/Components/Patient/PatientFilter.tsx +++ b/src/Components/Patient/PatientFilter.tsx @@ -81,8 +81,8 @@ export default function PatientFilter(props: any) { : [], last_consultation_current_bed__location: filter.last_consultation_current_bed__location || "", - last_consultation_discharge_reason: - filter.last_consultation_discharge_reason || null, + last_consultation__new_discharge_reason: + filter.last_consultation__new_discharge_reason || null, srf_id: filter.srf_id || null, number_of_doses: filter.number_of_doses || null, covin_id: filter.covin_id || null, @@ -241,7 +241,7 @@ export default function PatientFilter(props: any) { last_consultation_discharge_date_before, last_consultation_discharge_date_after, last_consultation_admitted_bed_type_list, - last_consultation_discharge_reason, + last_consultation__new_discharge_reason, last_consultation_current_bed__location, number_of_doses, covin_id, @@ -298,8 +298,8 @@ export default function PatientFilter(props: any) { age_max: age_max || "", last_consultation_admitted_bed_type_list: last_consultation_admitted_bed_type_list || [], - last_consultation_discharge_reason: - last_consultation_discharge_reason || "", + last_consultation__new_discharge_reason: + last_consultation__new_discharge_reason || "", srf_id: srf_id || "", number_of_doses: number_of_doses || "", covin_id: covin_id || "", @@ -424,16 +424,16 @@ export default function PatientFilter(props: any) {
Discharge Reason o.id} optionLabel={(o) => o.text} onChange={(o) => setFilterState({ ...filterState, - last_consultation_discharge_reason: o, + last_consultation__new_discharge_reason: o, }) } /> diff --git a/src/Components/Patient/PatientHome.tsx b/src/Components/Patient/PatientHome.tsx index 32118e5951e..22cc35a170e 100644 --- a/src/Components/Patient/PatientHome.tsx +++ b/src/Components/Patient/PatientHome.tsx @@ -1,7 +1,11 @@ import { navigate } from "raviger"; import { lazy, useCallback, useEffect, useState } from "react"; import { useDispatch } from "react-redux"; -import { GENDER_TYPES, SAMPLE_TEST_STATUS } from "../../Common/constants"; +import { + DISCHARGE_REASONS, + GENDER_TYPES, + SAMPLE_TEST_STATUS, +} from "../../Common/constants"; import { statusType, useAbortableEffect } from "../../Common/utils"; import { getConsultationList, @@ -761,7 +765,8 @@ export const PatientHome = (props: any) => {
- {patientData.last_consultation?.discharge_reason === "EXP" && ( + {patientData.last_consultation?.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id && (
i.text == "Expired")?.id ? ( {" "} Expired on {formatDate(consultation?.death_datetime)} @@ -379,17 +380,18 @@ export default function PatientInfoCard(props: { Discharge Reason
- {!consultation?.discharge_reason ? ( + {!consultation?.new_discharge_reason ? ( {consultation.suggestion === "OP" ? "OP file closed" : "UNKNOWN"} - ) : consultation?.discharge_reason === "EXP" ? ( + ) : consultation?.new_discharge_reason === + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id ? ( EXPIRED ) : ( DISCHARGE_REASONS.find( - (reason) => reason.id === consultation?.discharge_reason + (reason) => reason.id === consultation?.new_discharge_reason )?.text )}
diff --git a/src/Components/Shifting/ShiftDetailsUpdate.tsx b/src/Components/Shifting/ShiftDetailsUpdate.tsx index 36d2d120585..a726150c9e1 100644 --- a/src/Components/Shifting/ShiftDetailsUpdate.tsx +++ b/src/Components/Shifting/ShiftDetailsUpdate.tsx @@ -2,6 +2,7 @@ import * as Notification from "../../Utils/Notifications.js"; import { BREATHLESSNESS_LEVEL, + DISCHARGE_REASONS, FACILITY_TYPES, PATIENT_CATEGORIES, SHIFTING_CHOICES_PEACETIME, @@ -282,7 +283,9 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { show={showDischargeModal} onClose={() => setShowDischargeModal(false)} consultationData={consultationData} - discharge_reason="EXP" + new_discharge_reason={ + DISCHARGE_REASONS.find((i) => i.text == "Expired")?.id + } afterSubmit={() => { handleSubmit(true); }} From e1879773a86644acfacd3f03ed17f9a7ac54eb45 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Thu, 11 Jan 2024 22:03:02 +0530 Subject: [PATCH 03/11] Adds user type: Nurse and Nurse (readonly) (#6464) * adds nurse user type, fixes #6240 * Nurse/Staff can now create doctors * Staff user: disallow link in patient list * fixes #7002; restrict access to external results for Nurse and Staff * Hide External Results tab and pages from Staff and Nurse --------- Co-authored-by: Mohammed Nihal <57055998+nihal467@users.noreply.github.com> Co-authored-by: Aakash Singh --- README.md | 2 +- src/Common/constants.tsx | 22 ++-------- src/Components/Common/Sidebar/Sidebar.tsx | 41 +++++++++++-------- src/Components/ExternalResult/ResultList.tsx | 26 ++++++++---- .../Facility/DoctorVideoSlideover.tsx | 4 +- src/Components/Patient/ManagePatients.tsx | 22 +++++++--- src/Components/Patient/PatientRegister.tsx | 6 +++ src/Components/Users/UserAdd.tsx | 31 +++++++++----- src/Routers/AppRouter.tsx | 11 ++++- 9 files changed, 103 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 7f2c3e926bd..c1dd7f37564 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Authenticate to staging API with any of the following credentials - username: staffdev password: Coronasafe@123 - role: Staff + role: Nurse - username: doctordev password: Coronasafe@123 diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 55a35bef9ab..3cb972641b5 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -28,6 +28,8 @@ export type UserRole = | "Volunteer" | "StaffReadOnly" | "Staff" + | "NurseReadOnly" + | "Nurse" | "Doctor" | "WardAdmin" | "LocalBodyAdmin" @@ -47,6 +49,8 @@ export const USER_TYPE_OPTIONS: { { id: "Volunteer", role: "Volunteer", readOnly: false }, { id: "StaffReadOnly", role: "Staff", readOnly: true }, { id: "Staff", role: "Staff", readOnly: false }, + { id: "NurseReadOnly", role: "Nurse", readOnly: true }, + { id: "Nurse", role: "Nurse", readOnly: false }, { id: "Doctor", role: "Doctor", readOnly: false }, { id: "WardAdmin", role: "Ward Admin", readOnly: false }, { id: "LocalBodyAdmin", role: "Local Body Admin", readOnly: false }, @@ -418,24 +422,6 @@ export const SAMPLE_FLOW_RULES = { RECEIVED_AT_LAB: ["COMPLETED"], }; -export const ROLE_STATUS_MAP = { - Staff: ["SENT_TO_COLLECTON_CENTRE"], - DistrictAdmin: [ - "APPROVED", - "DENIED", - "SENT_TO_COLLECTON_CENTRE", - "RECEIVED_AND_FORWARED", - ], - StateLabAdmin: [ - "APPROVED", - "DENIED", - "SENT_TO_COLLECTON_CENTRE", - "RECEIVED_AND_FORWARED", - "RECEIVED_AT_LAB", - "COMPLETED", - ], -}; - export const DISEASE_STATUS = [ "POSITIVE", "SUSPECTED", diff --git a/src/Components/Common/Sidebar/Sidebar.tsx b/src/Components/Common/Sidebar/Sidebar.tsx index fa809571772..9d378df4267 100644 --- a/src/Components/Common/Sidebar/Sidebar.tsx +++ b/src/Components/Common/Sidebar/Sidebar.tsx @@ -8,6 +8,7 @@ import useConfig from "../../../Common/hooks/useConfig"; import SlideOver from "../../../CAREUI/interactive/SlideOver"; import { classNames } from "../../../Utils/utils"; import { Link } from "raviger"; +import useAuthUser from "../../../Common/hooks/useAuthUser"; export const SIDEBAR_SHRINK_PREFERENCE_KEY = "sidebarShrinkPreference"; @@ -27,28 +28,36 @@ type StatelessSidebarProps = onItemClick: (open: boolean) => void; }; -const NavItems = [ - { text: "Facilities", to: "/facility", icon: "care-l-hospital" }, - { text: "Patients", to: "/patients", icon: "care-l-user-injured" }, - { text: "Assets", to: "/assets", icon: "care-l-shopping-cart-alt" }, - { text: "Sample Test", to: "/sample", icon: "care-l-medkit" }, - { text: "Shifting", to: "/shifting", icon: "care-l-ambulance" }, - { text: "Resource", to: "/resource", icon: "care-l-heart-medical" }, - { - text: "External Results", - to: "/external_results", - icon: "care-l-clipboard-notes", - }, - { text: "Users", to: "/users", icon: "care-l-users-alt" }, - { text: "Notice Board", to: "/notice_board", icon: "care-l-meeting-board" }, -]; - const StatelessSidebar = ({ shrinkable = false, shrinked = false, setShrinked, onItemClick, }: StatelessSidebarProps) => { + const authUser = useAuthUser(); + + const NavItems = [ + { text: "Facilities", to: "/facility", icon: "care-l-hospital" }, + { text: "Patients", to: "/patients", icon: "care-l-user-injured" }, + { text: "Assets", to: "/assets", icon: "care-l-shopping-cart-alt" }, + { text: "Sample Test", to: "/sample", icon: "care-l-medkit" }, + { text: "Shifting", to: "/shifting", icon: "care-l-ambulance" }, + { text: "Resource", to: "/resource", icon: "care-l-heart-medical" }, + ...(!["Nurse", "NurseReadOnly", "Staff", "StaffReadOnly"].includes( + authUser.user_type + ) + ? [ + { + text: "External Results", + to: "/external_results", + icon: "care-l-clipboard-notes", + }, + ] + : []), + { text: "Users", to: "/users", icon: "care-l-users-alt" }, + { text: "Notice Board", to: "/notice_board", icon: "care-l-meeting-board" }, + ]; + const { main_logo } = useConfig(); const activeLink = useActiveLink(); const Item = shrinked ? ShrinkedSidebarItem : SidebarItem; diff --git a/src/Components/ExternalResult/ResultList.tsx b/src/Components/ExternalResult/ResultList.tsx index 8fddc7cadc9..9a04fcf38b6 100644 --- a/src/Components/ExternalResult/ResultList.tsx +++ b/src/Components/ExternalResult/ResultList.tsx @@ -16,9 +16,12 @@ import Page from "../Common/components/Page"; import routes from "../../Redux/api"; import useQuery from "../../Utils/request/useQuery"; import { parsePhoneNumber } from "../../Utils/utils"; +import useAuthUser from "../../Common/hooks/useAuthUser"; +import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; const Loading = lazy(() => import("../Common/Loading")); export default function ResultList() { + const authUser = useAuthUser(); const { qParams, updateQuery, @@ -165,6 +168,10 @@ export default function ResultList() { { @@ -226,13 +233,18 @@ export default function ResultList() { navigate("/external_results/upload"), - options: { - icon: , - }, - }, + ...(authUser.user_type !== "Nurse" && + authUser.user_type !== "Staff" + ? [ + { + label: "Import Results", + action: () => navigate("/external_results/upload"), + options: { + icon: , + }, + }, + ] + : []), { label: "Export Results", action: () => diff --git a/src/Components/Facility/DoctorVideoSlideover.tsx b/src/Components/Facility/DoctorVideoSlideover.tsx index f16b7ba1811..c2cd94e0ded 100644 --- a/src/Components/Facility/DoctorVideoSlideover.tsx +++ b/src/Components/Facility/DoctorVideoSlideover.tsx @@ -62,8 +62,8 @@ export default function DoctorVideoSlideover(props: { home: true, }, { - title: "Staff", - user_type: "Staff", + title: "Nurse", + user_type: "Nurse", home: true, }, { diff --git a/src/Components/Patient/ManagePatients.tsx b/src/Components/Patient/ManagePatients.tsx index af5c5398cb0..0f9a6656f67 100644 --- a/src/Components/Patient/ManagePatients.tsx +++ b/src/Components/Patient/ManagePatients.tsx @@ -537,12 +537,9 @@ export const PatientManager = () => { ? PATIENT_CATEGORIES.find((c) => c.text === category)?.twClass : "patient-unknown"; - return ( -
{
)}
+
+ ); + + if ( + authUser.user_type === "Staff" || + authUser.user_type === "StaffReadOnly" + ) { + return children; + } + + return ( + + {children} ); }); diff --git a/src/Components/Patient/PatientRegister.tsx b/src/Components/Patient/PatientRegister.tsx index 546d35cc487..ce63d5019de 100644 --- a/src/Components/Patient/PatientRegister.tsx +++ b/src/Components/Patient/PatientRegister.tsx @@ -70,6 +70,7 @@ import useConfig from "../../Common/hooks/useConfig"; import { useDispatch } from "react-redux"; import { validatePincode } from "../../Common/validation"; import { FormContextValue } from "../Form/FormContext.js"; +import useAuthUser from "../../Common/hooks/useAuthUser.js"; const Loading = lazy(() => import("../Common/Loading")); const PageTitle = lazy(() => import("../Common/PageTitle")); @@ -180,6 +181,7 @@ const patientFormReducer = (state = initialState, action: any) => { }; export const PatientRegister = (props: PatientRegisterProps) => { + const authUser = useAuthUser(); const { goBack } = useAppHistory(); const { gov_data_api_key, enable_hcx, enable_abdm } = useConfig(); const dispatchAction: any = useDispatch(); @@ -1173,6 +1175,10 @@ export const PatientRegister = (props: PatientRegisterProps) => {
{ setShowImport({ show: true, diff --git a/src/Components/Users/UserAdd.tsx b/src/Components/Users/UserAdd.tsx index 26ba9e44b4b..2c5319e67f3 100644 --- a/src/Components/Users/UserAdd.tsx +++ b/src/Components/Users/UserAdd.tsx @@ -99,6 +99,13 @@ const initForm: UserForm = { doctor_medical_council_registration: undefined, }; +const STAFF_OR_NURSE_USER = [ + "Staff", + "StaffReadOnly", + "Nurse", + "NurseReadOnly", +]; + const initError = Object.assign( {}, ...Object.keys(initForm).map((k) => ({ [k]: "" })) @@ -234,15 +241,19 @@ export const UserAdd = (props: UserProps) => { : // Exception to allow Staff to Create Doctors defaultAllowedUserTypes; + // TODO: refactor lines 227 through 248 to be more readable. This is messy. + if (authUser.user_type === "Nurse" || authUser.user_type === "Staff") { + userTypes.push(USER_TYPE_OPTIONS[6]); // Temperorily allows creation of users with elevated permissions due to introduction of new roles. + } + const headerText = !userId ? "Add User" : "Update User"; const buttonText = !userId ? "Save User" : "Update Details"; - const showLocalbody = !( - state.form.user_type === "Pharmacist" || - state.form.user_type === "Volunteer" || - state.form.user_type === "Doctor" || - state.form.user_type === "Staff" || - state.form.user_type === "StaffReadOnly" - ); + const showLocalbody = ![ + "Pharmacist", + "Volunteer", + "Doctor", + ...STAFF_OR_NURSE_USER, + ].includes(state.form.user_type); const { loading: isDistrictLoading } = useQuery(routes.getDistrictByState, { prefetch: !!(selectedStateId > 0), @@ -332,10 +343,8 @@ export const UserAdd = (props: UserProps) => { case "facilities": if ( state.form[field].length === 0 && - (authUser.user_type === "Staff" || - authUser.user_type === "StaffReadOnly") && - (state.form["user_type"] === "Staff" || - state.form["user_type"] === "StaffReadOnly") + STAFF_OR_NURSE_USER.includes(authUser.user_type) && + STAFF_OR_NURSE_USER.includes(state.form.user_type) ) { errors[field] = "Please select atleast one of the facilities you are linked to"; diff --git a/src/Routers/AppRouter.tsx b/src/Routers/AppRouter.tsx index 5e238627bc8..56d8b31573b 100644 --- a/src/Routers/AppRouter.tsx +++ b/src/Routers/AppRouter.tsx @@ -25,13 +25,13 @@ import AssetRoutes from "./routes/AssetRoutes"; import ResourceRoutes from "./routes/ResourceRoutes"; import ExternalResultRoutes from "./routes/ExternalResultRoutes"; import { DetailRoute } from "./types"; +import useAuthUser from "../Common/hooks/useAuthUser"; const Routes = { "/": () => , ...AssetRoutes, ...ConsultationRoutes, - ...ExternalResultRoutes, ...FacilityRoutes, ...PatientRoutes, ...ResourceRoutes, @@ -49,6 +49,7 @@ const Routes = { }; export default function AppRouter() { + const authUser = useAuthUser(); const { main_logo, enable_hcx } = useConfig(); let routes = Routes; @@ -57,6 +58,14 @@ export default function AppRouter() { routes = { ...routes, ...HCXRoutes }; } + if ( + !["Nurse", "NurseReadOnly", "Staff", "StaffReadOnly"].includes( + authUser.user_type + ) + ) { + routes = { ...routes, ...ExternalResultRoutes }; + } + useRedirect("/user", "/users"); const pages = useRoutes(routes) || ; const path = usePath(); From dfe99238be335f9dbd86644416fbf358d6e43e05 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Fri, 12 Jan 2024 11:58:56 +0530 Subject: [PATCH 04/11] Fixes incorrect loading state when login form is invalid (no credentials provided) (#7011) * Fix incorrect loading state when login form is invalid * fix bugs --- src/Components/Auth/Login.tsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Components/Auth/Login.tsx b/src/Components/Auth/Login.tsx index c2acb88cf0c..371a3510859 100644 --- a/src/Components/Auth/Login.tsx +++ b/src/Components/Auth/Login.tsx @@ -92,15 +92,14 @@ export const Login = (props: { forgot?: boolean }) => { const handleSubmit = async (e: any) => { e.preventDefault(); - setLoading(true); invalidateFiltersCache(); - const validated = validateData(); - if (!validated) return; - + if (!validated) { + setLoading(false); + return; + } const { res } = await signIn(validated); - setCaptcha(res?.status === 429); setLoading(false); }; From 066789417ea846ffd5e96a8bc3de7b8be8907d8e Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Fri, 12 Jan 2024 12:10:33 +0530 Subject: [PATCH 05/11] prevent resetting the filters cache on page load (#7009) * fixes #7008; fix resetting the filters on page visit * fix existing filters not being cleared * fix --- src/Common/hooks/useFilters.tsx | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/Common/hooks/useFilters.tsx b/src/Common/hooks/useFilters.tsx index 5781eaca54f..117a14ba418 100644 --- a/src/Common/hooks/useFilters.tsx +++ b/src/Common/hooks/useFilters.tsx @@ -1,4 +1,4 @@ -import { useQueryParams } from "raviger"; +import { QueryParam, setQueryParamsOptions, useQueryParams } from "raviger"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import GenericFilterBadge from "../../CAREUI/display/FilterBadge"; @@ -23,7 +23,15 @@ export default function useFilters({ limit = 14 }: { limit?: number }) { const { kasp_string } = useConfig(); const hasPagination = limit > 0; const [showFilters, setShowFilters] = useState(false); - const [qParams, setQueryParams] = useQueryParams(); + const [qParams, _setQueryParams] = useQueryParams(); + + const setQueryParams = ( + query: QueryParam, + options?: setQueryParamsOptions + ) => { + _setQueryParams(query, options); + updateFiltersCache(query); + }; const updateQuery = (filter: FilterState) => { filter = hasPagination ? { page: 1, limit, ...filter } : filter; @@ -38,8 +46,6 @@ export default function useFilters({ limit = 14 }: { limit?: number }) { }; const removeFilter = (param: string) => removeFilters([param]); - useEffect(() => updateFiltersCache(qParams), [qParams]); - useEffect(() => { const cache = getFiltersCache(); const qParamKeys = Object.keys(qParams); From 2fbdb85847e8d4657a916e2a7666a4bc9043838c Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Fri, 12 Jan 2024 17:31:12 +0530 Subject: [PATCH 06/11] Update weekly_working_hours to allow null value (#7015) --- src/Components/Users/UserProfile.tsx | 23 ++++++++++++----------- src/Components/Users/models.tsx | 2 +- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/Components/Users/UserProfile.tsx b/src/Components/Users/UserProfile.tsx index 1fcc8fb385e..170de558e41 100644 --- a/src/Components/Users/UserProfile.tsx +++ b/src/Components/Users/UserProfile.tsx @@ -34,7 +34,7 @@ type EditForm = { doctor_qualification: string | undefined; doctor_experience_commenced_on: number | string | undefined; doctor_medical_council_registration: string | undefined; - weekly_working_hours: string | undefined; + weekly_working_hours: string | null; }; type ErrorForm = { firstName: string; @@ -253,13 +253,11 @@ export default function UserProfile() { } return; case "weekly_working_hours": - if (!states.form[field]) { - errors[field] = "This field is required"; - invalidForm = true; - } else if ( - Number(states.form[field]) < 0 || - Number(states.form[field]) > 168 || - !/^\d+$/.test(states.form[field] ?? "") + if ( + states.form[field] && + (Number(states.form[field]) < 0 || + Number(states.form[field]) > 168 || + !/^\d+$/.test(states.form[field] ?? "")) ) { errors[field] = "Average weekly working hours must be a number between 0 and 168"; @@ -331,7 +329,11 @@ export default function UserProfile() { states.form.user_type === "Doctor" ? states.form.doctor_medical_council_registration : undefined, - weekly_working_hours: states.form.weekly_working_hours, + weekly_working_hours: + states.form.weekly_working_hours && + states.form.weekly_working_hours !== "" + ? states.form.weekly_working_hours + : null, }; const { res } = await request(routes.partialUpdateUser, { pathParams: { username: authUser.username }, @@ -588,7 +590,7 @@ export default function UserProfile() { Average weekly working hours
- {userData?.weekly_working_hours || "-"} + {userData?.weekly_working_hours ?? "-"}
Date: Fri, 12 Jan 2024 17:31:44 +0530 Subject: [PATCH 07/11] Update AssetFilter component to handle null facility value (#7014) --- src/Components/Assets/AssetFilter.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Components/Assets/AssetFilter.tsx b/src/Components/Assets/AssetFilter.tsx index 9aeac85e734..5d2ad9e30ae 100644 --- a/src/Components/Assets/AssetFilter.tsx +++ b/src/Components/Assets/AssetFilter.tsx @@ -20,7 +20,7 @@ const getDate = (value: any) => function AssetFilter(props: any) { const { filter, onChange, closeFilter, removeFilters } = props; - const [facility, setFacility] = useState({ name: "" }); + const [facility, setFacility] = useState(null); const [asset_type, setAssetType] = useState( filter.asset_type ? filter.asset_type : "" ); @@ -51,7 +51,7 @@ function AssetFilter(props: any) { setLocationId( facility?.id === qParams.facility ? qParams.location ?? "" : "" ); - }, [facility.id, qParams.facility, qParams.location]); + }, [facility?.id, qParams.facility, qParams.location]); const clearFilter = useCallback(() => { removeFilters([ @@ -81,8 +81,8 @@ function AssetFilter(props: any) { onChange(data); }; - const handleFacilitySelect = (selected: FacilityModel) => { - setFacility(selected ? selected : facility); + const handleFacilitySelect = (selected: FacilityModel | null) => { + setFacility(selected); handleLocationSelect(""); }; const handleLocationSelect = (selectedId: string) => { @@ -107,7 +107,7 @@ function AssetFilter(props: any) { - handleFacilitySelect(selected as FacilityModel) + handleFacilitySelect(selected as FacilityModel | null) } selected={facility} errors="" From b88233173d751b4ee181d12ca070e1ad13c1e208 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Fri, 12 Jan 2024 20:14:14 +0530 Subject: [PATCH 08/11] fixes #7018; nurse undefined in doctor notes (#7020) --- src/Common/constants.tsx | 4 +++- src/Components/Facility/models.tsx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 3cb972641b5..00ce057784b 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -1028,6 +1028,8 @@ export const USER_TYPES_MAP = { StaffReadOnly: "Staff", Staff: "Staff", Doctor: "Doctor", + Nurse: "Nurse", + NurseReadOnly: "Nurse", WardAdmin: "Ward Admin", LocalBodyAdmin: "Local Body Admin", DistrictLabAdmin: "District Lab Admin", @@ -1037,7 +1039,7 @@ export const USER_TYPES_MAP = { StateReadOnlyAdmin: "State Admin", StateAdmin: "State Admin", RemoteSpecialist: "Remote Specialist", -}; +} as const; export const AREACODES: Record = { CA: [ diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index f14a3a102ee..99d91c6cbae 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -5,7 +5,7 @@ import { AssetData, AssetLocationType } from "../Assets/AssetTypes"; import { UserBareMinimum } from "../Users/models"; import { RouteToFacility } from "../Common/RouteToFacilitySelect"; import { ConsultationDiagnosis, CreateDiagnosis } from "../Diagnosis/types"; -import { ConsultationSuggestionValue } from "../../Common/constants"; +import { ConsultationSuggestionValue, UserRole } from "../../Common/constants"; export interface LocalBodyModel { id: number; @@ -494,7 +494,7 @@ export interface PatientNotesModel { note: string; facility: BaseFacilityModel; created_by_object: BaseUserModel; - user_type?: string; + user_type?: UserRole | "RemoteSpecialist"; created_date: string; } From 33f913c1d77502b844efc10ff89db47548a7cc9b Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Fri, 12 Jan 2024 20:24:52 +0530 Subject: [PATCH 09/11] Reset preview image and close modal for camera (#7024) --- src/Components/Patient/FileUpload.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Components/Patient/FileUpload.tsx b/src/Components/Patient/FileUpload.tsx index e1ea368af72..d1dedc5e2ff 100644 --- a/src/Components/Patient/FileUpload.tsx +++ b/src/Components/Patient/FileUpload.tsx @@ -276,6 +276,7 @@ export const FileUpload = (props: FileUploadProps) => { const handleClose = () => { setDownloadURL(""); + setPreviewImage(null); setFileState({ ...file_state, open: false, @@ -1212,6 +1213,7 @@ export const FileUpload = (props: FileUploadProps) => { { + setPreviewImage(null); setModalOpenForCamera(false); }} className="m-2" @@ -1272,6 +1274,7 @@ export const FileUpload = (props: FileUploadProps) => { { setModalOpenForCamera(false); + setPreviewImage(null); }} > {t("submit")} From 27b524a22c7c6e09f7828fae7486c90129b9b910 Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Fri, 12 Jan 2024 23:55:20 +0530 Subject: [PATCH 10/11] Refactor user delete button in FacilityUsers and ManageUsers components (#7027) --- src/Components/Facility/FacilityUsers.tsx | 14 ++++++++------ src/Components/Users/ManageUsers.tsx | 16 ++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Components/Facility/FacilityUsers.tsx b/src/Components/Facility/FacilityUsers.tsx index ca2e1d0e363..f18b9578192 100644 --- a/src/Components/Facility/FacilityUsers.tsx +++ b/src/Components/Facility/FacilityUsers.tsx @@ -296,7 +296,10 @@ export default function FacilityUsers(props: any) {
-
+
{`${user.first_name} ${user.last_name}`} {user.last_login && isUserOnline(user) ? ( @@ -306,13 +309,12 @@ export default function FacilityUsers(props: any) { > ) : null} {showUserDelete(authUser, user) && ( - + +
)}
diff --git a/src/Components/Users/ManageUsers.tsx b/src/Components/Users/ManageUsers.tsx index 750603ccf6f..300f9846a89 100644 --- a/src/Components/Users/ManageUsers.tsx +++ b/src/Components/Users/ManageUsers.tsx @@ -218,7 +218,10 @@ export default function ManageUsers() { )} -
+
{`${user.first_name} ${user.last_name}`} {user.last_login && cur_online ? ( @@ -228,15 +231,12 @@ export default function ManageUsers() { > ) : null} {showUserDelete(authUser, user) && ( - handleDelete(user)} > - Delete - + +
)}
From 51b02c26666877f2b0b481f0476a9b34acba6743 Mon Sep 17 00:00:00 2001 From: Ashesh <3626859+Ashesh3@users.noreply.github.com> Date: Sat, 13 Jan 2024 00:10:40 +0530 Subject: [PATCH 11/11] Fix InvestigationTable alignment (#7028) --- src/Components/Facility/Investigations/InvestigationTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Components/Facility/Investigations/InvestigationTable.tsx b/src/Components/Facility/Investigations/InvestigationTable.tsx index 54e7e50bdc7..d322698779d 100644 --- a/src/Components/Facility/Investigations/InvestigationTable.tsx +++ b/src/Components/Facility/Investigations/InvestigationTable.tsx @@ -17,7 +17,7 @@ const TestRow = ({ data, i, onChange, showForm, value, isChanged }: any) => { {data?.investigation_object?.name || "---"} - + {showForm ? ( data?.investigation_object?.investigation_type === "Choice" ? (