From 788e7529ab8c3d333a345e64592d76d435344d42 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Mon, 2 Sep 2024 17:58:08 +0530 Subject: [PATCH 01/19] Fix allowed extensions not working for file input (#8419) --- src/Utils/useFileUpload.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utils/useFileUpload.tsx b/src/Utils/useFileUpload.tsx index 49e4f9d99bd..ad8e185745c 100644 --- a/src/Utils/useFileUpload.tsx +++ b/src/Utils/useFileUpload.tsx @@ -248,7 +248,7 @@ export default function useFileUpload( type="file" accept={ "allowedExtensions" in options - ? options.allowedExtensions?.join(",") + ? options.allowedExtensions?.map((e) => "." + e).join(",") : "allowAllExtensions" in options ? "*" : DEFAULT_ALLOWED_EXTENSIONS.join(",") From dcedaa9a44f5caa596eb450042c9c1e93f447b0c Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Mon, 2 Sep 2024 17:59:22 +0530 Subject: [PATCH 02/19] Fixed icd11 diagnosis multiselect field making unnecessary requests (#8448) Co-authored-by: rithviknishad --- .../AddICD11Diagnosis.tsx | 1 + .../Form/FormFields/AutocompleteMultiselect.tsx | 12 ++++++++++-- src/Components/Patient/DiagnosesFilter.tsx | 2 ++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx index 39f35a4fcf4..66e0592511c 100644 --- a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx +++ b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx @@ -26,6 +26,7 @@ export default function AddICD11Diagnosis(props: AddICD11DiagnosisProps) { const hasError = !!props.disallowed.find((d) => d?.id === selected?.id); const { res, data, loading, refetch } = useQuery(routes.listICD11Diagnosis, { + prefetch: false, silent: true, }); diff --git a/src/Components/Form/FormFields/AutocompleteMultiselect.tsx b/src/Components/Form/FormFields/AutocompleteMultiselect.tsx index 7fea2d4c6e7..4e7892a6a64 100644 --- a/src/Components/Form/FormFields/AutocompleteMultiselect.tsx +++ b/src/Components/Form/FormFields/AutocompleteMultiselect.tsx @@ -26,6 +26,7 @@ type AutocompleteMultiSelectFormFieldProps = FormFieldBaseProps & { optionDisabled?: OptionCallback; onQuery?: (query: string) => void; dropdownIcon?: React.ReactNode | undefined; + minQueryLength?: number; isLoading?: boolean; selectAll?: boolean; }; @@ -64,6 +65,7 @@ type AutocompleteMutliSelectProps = { isLoading?: boolean; selectAll?: boolean; error?: string; + minQueryLength?: number; }; /** @@ -80,7 +82,9 @@ export const AutocompleteMutliSelect = ( const [query, setQuery] = useState(""); // Ensure lower case const comboButtonRef = useRef(null); useEffect(() => { - props.onQuery && props.onQuery(query); + query.length >= (props.minQueryLength || 1) && + props.onQuery && + props.onQuery(query); }, [query]); const handleSingleSelect = (o: any) => { if (o.option?.isSingleSelect === true && comboButtonRef.current) { @@ -175,7 +179,11 @@ export const AutocompleteMutliSelect = ( as="ul" className="cui-dropdown-base absolute top-12 z-10 mt-0.5" > - {props.isLoading ? ( + {props.minQueryLength && query.length < props.minQueryLength ? ( +
+ {`Please enter at least ${props.minQueryLength} characters to search`} +
+ ) : props.isLoading ? ( ) : filteredOptions.length ? ( <> diff --git a/src/Components/Patient/DiagnosesFilter.tsx b/src/Components/Patient/DiagnosesFilter.tsx index c4c4872fdda..1217e821389 100644 --- a/src/Components/Patient/DiagnosesFilter.tsx +++ b/src/Components/Patient/DiagnosesFilter.tsx @@ -37,6 +37,7 @@ export default function DiagnosesFilter(props: Props) { const [diagnoses, setDiagnoses] = useState([]); const { res, data, loading, refetch } = useQuery(routes.listICD11Diagnosis, { silent: true, + prefetch: false, }); useEffect(() => { @@ -72,6 +73,7 @@ export default function DiagnosesFilter(props: Props) { name="icd11_search" className="w-full" placeholder={t("search_icd11_placeholder")} + minQueryLength={2} value={diagnoses} onChange={(e) => { setDiagnoses(e.value); From 93347f4dfaa5d7ec7f1475b6e0e80b646cf95bfa Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Mon, 2 Sep 2024 18:00:18 +0530 Subject: [PATCH 03/19] Switched to uploadFile from axios in scribe (#8379) --- src/Components/Scribe/Scribe.tsx | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/src/Components/Scribe/Scribe.tsx b/src/Components/Scribe/Scribe.tsx index c942d130a43..cc57ee39dde 100644 --- a/src/Components/Scribe/Scribe.tsx +++ b/src/Components/Scribe/Scribe.tsx @@ -5,10 +5,10 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; import routes from "../../Redux/api"; import * as Notify from "../../Utils/Notifications"; import request from "../../Utils/request/request"; -import axios from "axios"; import { UserModel } from "../Users/models"; import useConfig from "../../Common/hooks/useConfig"; import useSegmentedRecording from "../../Utils/useSegmentedRecorder"; +import uploadFile from "../../Utils/request/uploadFile"; interface FieldOption { id: string | number; @@ -120,21 +120,20 @@ export const Scribe: React.FC = ({ form, onFormUpdate }) => { return; } const newFile = new File([f], `${internal_name}`, { type: f.type }); - const config = { - headers: { - "Content-type": newFile?.type?.split(";")?.[0], - "Content-disposition": "inline", - }, + const headers = { + "Content-type": newFile?.type?.split(";")?.[0], + "Content-disposition": "inline", }; - axios - .put(url, newFile, config) - .then(() => { - resolve(); - }) - .catch((error) => { - reject(error); - }); + uploadFile( + url, + newFile, + "PUT", + headers, + (xhr: XMLHttpRequest) => (xhr.status === 200 ? resolve() : reject()), + null, + reject, + ); }); }; From 43de4e725bac94ac6554dd8a15754a15b46baba7 Mon Sep 17 00:00:00 2001 From: Khavin Shankar Date: Mon, 2 Sep 2024 19:11:38 +0530 Subject: [PATCH 04/19] enable interaction with other components when a dropdown is open (#8432) --- src/Components/CameraFeed/AssetBedSelect.tsx | 6 ++++-- src/Components/Common/components/Menu.tsx | 7 ++++--- src/Components/Form/AutoCompleteAsync.tsx | 12 +++++++----- src/Components/Form/FormFields/Autocomplete.tsx | 10 ++++++---- .../Form/FormFields/AutocompleteMultiselect.tsx | 16 +++++++++------- src/Components/Form/MultiSelectMenuV2.tsx | 6 ++++-- src/Components/Form/SelectMenuV2.tsx | 11 ++++++++--- 7 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/Components/CameraFeed/AssetBedSelect.tsx b/src/Components/CameraFeed/AssetBedSelect.tsx index b97675c7b31..18bef259b2b 100644 --- a/src/Components/CameraFeed/AssetBedSelect.tsx +++ b/src/Components/CameraFeed/AssetBedSelect.tsx @@ -1,14 +1,15 @@ -import { AssetBedModel } from "../Assets/AssetTypes"; import { Listbox, ListboxButton, ListboxOption, ListboxOptions, } from "@headlessui/react"; + +import { AssetBedModel } from "../Assets/AssetTypes"; +import ButtonV2 from "../Common/components/ButtonV2"; import CareIcon from "../../CAREUI/icons/CareIcon"; import { classNames } from "../../Utils/utils"; import { dropdownOptionClassNames } from "../Form/MultiSelectMenuV2"; -import ButtonV2 from "../Common/components/ButtonV2"; interface Props { disabled?: boolean; @@ -108,6 +109,7 @@ export const CameraPresetDropdown = ( diff --git a/src/Components/Common/components/Menu.tsx b/src/Components/Common/components/Menu.tsx index 166b55737ea..14dabfe9781 100644 --- a/src/Components/Common/components/Menu.tsx +++ b/src/Components/Common/components/Menu.tsx @@ -1,9 +1,9 @@ import { Anyone, AuthorizedElementProps } from "../../../Utils/AuthorizeFor"; - import { ButtonSize, ButtonVariant } from "./ButtonV2"; -import CareIcon from "../../../CAREUI/icons/CareIcon"; -import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; import { DetailedHTMLProps, HTMLAttributes, ReactNode } from "react"; +import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; + +import CareIcon from "../../../CAREUI/icons/CareIcon"; import { classNames } from "../../../Utils/utils"; import { useIsAuthorized } from "../../../Common/hooks/useIsAuthorized"; @@ -51,6 +51,7 @@ export default function DropdownMenu({ <>{props.children} diff --git a/src/Components/Form/AutoCompleteAsync.tsx b/src/Components/Form/AutoCompleteAsync.tsx index 9b4c1f876e4..1cd07a93df1 100644 --- a/src/Components/Form/AutoCompleteAsync.tsx +++ b/src/Components/Form/AutoCompleteAsync.tsx @@ -1,4 +1,3 @@ -import { useEffect, useState, useMemo } from "react"; import { Combobox, ComboboxButton, @@ -6,15 +5,17 @@ import { ComboboxOption, ComboboxOptions, } from "@headlessui/react"; -import { debounce } from "lodash-es"; -import { DropdownTransition } from "../Common/components/HelperComponents"; -import CareIcon from "../../CAREUI/icons/CareIcon"; import { MultiSelectOptionChip, dropdownOptionClassNames, } from "./MultiSelectMenuV2"; -import { useTranslation } from "react-i18next"; +import { useEffect, useMemo, useState } from "react"; + +import CareIcon from "../../CAREUI/icons/CareIcon"; +import { DropdownTransition } from "../Common/components/HelperComponents"; import { classNames } from "../../Utils/utils"; +import { debounce } from "lodash-es"; +import { useTranslation } from "react-i18next"; interface Props { id?: string; @@ -153,6 +154,7 @@ const AutoCompleteAsync = (props: Props) => { diff --git a/src/Components/Form/FormFields/Autocomplete.tsx b/src/Components/Form/FormFields/Autocomplete.tsx index 7802a296201..2addbfa9b33 100644 --- a/src/Components/Form/FormFields/Autocomplete.tsx +++ b/src/Components/Form/FormFields/Autocomplete.tsx @@ -1,4 +1,3 @@ -import { useEffect, useState } from "react"; import { Combobox, ComboboxButton, @@ -6,12 +5,14 @@ import { ComboboxOption, ComboboxOptions, } from "@headlessui/react"; -import { DropdownTransition } from "../../Common/components/HelperComponents"; -import CareIcon from "../../../CAREUI/icons/CareIcon"; -import { dropdownOptionClassNames } from "../MultiSelectMenuV2"; import { FormFieldBaseProps, useFormFieldPropsResolver } from "./Utils"; +import { useEffect, useState } from "react"; + +import CareIcon from "../../../CAREUI/icons/CareIcon"; +import { DropdownTransition } from "../../Common/components/HelperComponents"; import FormField from "./FormField"; import { classNames } from "../../../Utils/utils"; +import { dropdownOptionClassNames } from "../MultiSelectMenuV2"; import { useTranslation } from "react-i18next"; type OptionCallback = (option: T) => R; @@ -209,6 +210,7 @@ export const Autocomplete = (props: AutocompleteProps) => { diff --git a/src/Components/Form/FormFields/AutocompleteMultiselect.tsx b/src/Components/Form/FormFields/AutocompleteMultiselect.tsx index 4e7892a6a64..4e9386afeba 100644 --- a/src/Components/Form/FormFields/AutocompleteMultiselect.tsx +++ b/src/Components/Form/FormFields/AutocompleteMultiselect.tsx @@ -1,10 +1,3 @@ -import { FormFieldBaseProps, useFormFieldPropsResolver } from "./Utils"; -import { - MultiSelectOptionChip, - dropdownOptionClassNames, -} from "../MultiSelectMenuV2"; -import { ReactNode, useEffect, useRef, useState } from "react"; -import CareIcon from "../../../CAREUI/icons/CareIcon"; import { Combobox, ComboboxButton, @@ -12,6 +5,14 @@ import { ComboboxOption, ComboboxOptions, } from "@headlessui/react"; +import { FormFieldBaseProps, useFormFieldPropsResolver } from "./Utils"; +import { + MultiSelectOptionChip, + dropdownOptionClassNames, +} from "../MultiSelectMenuV2"; +import { ReactNode, useEffect, useRef, useState } from "react"; + +import CareIcon from "../../../CAREUI/icons/CareIcon"; import { DropdownTransition } from "../../Common/components/HelperComponents"; import FormField from "./FormField"; import { classNames } from "../../../Utils/utils"; @@ -176,6 +177,7 @@ export const AutocompleteMutliSelect = ( diff --git a/src/Components/Form/MultiSelectMenuV2.tsx b/src/Components/Form/MultiSelectMenuV2.tsx index e728260e142..d3a46cdb1f9 100644 --- a/src/Components/Form/MultiSelectMenuV2.tsx +++ b/src/Components/Form/MultiSelectMenuV2.tsx @@ -1,4 +1,3 @@ -import CareIcon from "../../CAREUI/icons/CareIcon"; import { Label, Listbox, @@ -6,9 +5,11 @@ import { ListboxOption, ListboxOptions, } from "@headlessui/react"; -import { classNames } from "../../Utils/utils"; import { ReactNode, useRef } from "react"; +import CareIcon from "../../CAREUI/icons/CareIcon"; +import { classNames } from "../../Utils/utils"; + type OptionCallback = (option: T) => R; type Props = { @@ -135,6 +136,7 @@ const MultiSelectMenuV2 = (props: Props) => { diff --git a/src/Components/Form/SelectMenuV2.tsx b/src/Components/Form/SelectMenuV2.tsx index 379eb7b7315..cea890b7330 100644 --- a/src/Components/Form/SelectMenuV2.tsx +++ b/src/Components/Form/SelectMenuV2.tsx @@ -5,10 +5,11 @@ import { ListboxOption, ListboxOptions, } from "@headlessui/react"; + import CareIcon from "../../CAREUI/icons/CareIcon"; -import { dropdownOptionClassNames } from "./MultiSelectMenuV2"; -import { classNames } from "../../Utils/utils"; import { ReactNode } from "react"; +import { classNames } from "../../Utils/utils"; +import { dropdownOptionClassNames } from "./MultiSelectMenuV2"; type OptionCallback = (option: T) => R; @@ -131,7 +132,11 @@ const SelectMenuV2 = (props: SelectMenuProps) => { props.position === "above" ? "bottom-0 mb-12" : "top-0 mt-12", )} > - + {options.map((option, index) => ( Date: Mon, 2 Sep 2024 19:36:58 +0530 Subject: [PATCH 05/19] Fixes unknown option for heartbeat rhythm; adds i18n to vitals section (#8411) --- src/Common/constants.tsx | 10 +++- .../LogUpdate/CriticalCarePreview.tsx | 8 +++ src/Components/LogUpdate/Sections/Vitals.tsx | 55 +++++++++---------- src/Components/Patient/models.tsx | 4 +- src/Locale/en/Common.json | 1 + src/Locale/en/LogUpdate.json | 19 ++++++- 6 files changed, 62 insertions(+), 35 deletions(-) diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 6e5a9e33e4e..5fa8748b399 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -731,6 +731,12 @@ export const RESOURCE_FILTER_ORDER: Array = [ { id: 4, text: "-modified_date", desc: "DESC Modified Date" }, ]; +export const HEARTBEAT_RHYTHM_CHOICES = [ + "REGULAR", + "IRREGULAR", + "UNKNOWN", +] as const; + export const NURSING_CARE_PROCEDURES = [ "personal_hygiene", "positioning", @@ -792,12 +798,12 @@ export const RHYTHM_CHOICES = [ { id: 10, text: "IRREGULAR", desc: "Irregular" }, ] as const; -export const LOCATION_BED_TYPES: Array = [ +export const LOCATION_BED_TYPES = [ { id: "ISOLATION", name: "Isolation" }, { id: "ICU", name: "ICU" }, { id: "BED_WITH_OXYGEN_SUPPORT", name: "Bed with oxygen support" }, { id: "REGULAR", name: "Regular" }, -]; +] as const; export const ASSET_META_TYPE = [ { id: "CAMERA", text: "Camera(ONVIF)" }, diff --git a/src/Components/LogUpdate/CriticalCarePreview.tsx b/src/Components/LogUpdate/CriticalCarePreview.tsx index 00cbb47f69e..67ad693ff48 100644 --- a/src/Components/LogUpdate/CriticalCarePreview.tsx +++ b/src/Components/LogUpdate/CriticalCarePreview.tsx @@ -268,6 +268,14 @@ export default function CriticalCarePreview(props: Props) { }, ]} /> + +

Pain Scale

diff --git a/src/Components/LogUpdate/Sections/Vitals.tsx b/src/Components/LogUpdate/Sections/Vitals.tsx index 796575e4145..1fa4b3f3aac 100644 --- a/src/Components/LogUpdate/Sections/Vitals.tsx +++ b/src/Components/LogUpdate/Sections/Vitals.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { celsiusToFahrenheit, fahrenheitToCelsius, @@ -10,11 +11,12 @@ import RadioFormField from "../../Form/FormFields/RadioFormField"; import RangeFormField from "../../Form/FormFields/RangeFormField"; import TextAreaFormField from "../../Form/FormFields/TextAreaFormField"; import { FieldChangeEvent } from "../../Form/FormFields/Utils"; -import { DailyRoundsModel } from "../../Patient/models"; import PainChart from "../components/PainChart"; import { LogUpdateSectionMeta, LogUpdateSectionProps } from "../utils"; +import { HEARTBEAT_RHYTHM_CHOICES } from "../../../Common/constants"; const Vitals = ({ log, onChange }: LogUpdateSectionProps) => { + const { t } = useTranslation(); const handleBloodPressureChange = (event: FieldChangeEvent) => { const bp = { ...(log.bp ?? {}), @@ -27,11 +29,14 @@ const Vitals = ({ log, onChange }: LogUpdateSectionProps) => { return (
-

Blood Pressure

- MAP: {(log.bp?.mean && properRoundOf(log.bp.mean)) || "--"} +

{t("blood_pressure")}

+ + {t("map_acronym")}:{" "} + {(log.bp?.mean && properRoundOf(log.bp.mean)) || "--"} +
{ valueDescriptions={rangeValueDescription({ low: 99, high: 139 })} /> { />
- SpO2 - - } + label={t("spo2")} name="ventilator_spo2" //TODO: ensure whether this should be ventilator_spo2 itself or spo2 onChange={(c) => onChange({ ventilator_spo2: c.value })} value={log.ventilator_spo2} @@ -69,7 +70,7 @@ const Vitals = ({ log, onChange }: LogUpdateSectionProps) => { valueDescriptions={rangeValueDescription({ low: 89 })} /> onChange({ temperature: c.value })} value={log.temperature} @@ -87,7 +88,7 @@ const Vitals = ({ log, onChange }: LogUpdateSectionProps) => { ]} /> onChange({ resp: c.value })} value={log.resp} @@ -99,9 +100,9 @@ const Vitals = ({ log, onChange }: LogUpdateSectionProps) => { />
-

Pain

+

{t("pain")}

- Mark region and intensity of pain + {t("pain_chart_description")}
{ />
onChange({ pulse: c.value })} value={log.pulse} @@ -122,36 +123,30 @@ const Vitals = ({ log, onChange }: LogUpdateSectionProps) => { { till: 40, className: "text-red-500", - text: "Bradycardia", + text: t("bradycardia"), }, { till: 100, className: "text-green-500", - text: "Normal", + text: t("normal"), }, { className: "text-red-500", - text: "Tachycardia", + text: t("tachycardia"), }, ]} /> c.label} - optionValue={(c) => c.value || ""} + options={HEARTBEAT_RHYTHM_CHOICES} + optionDisplay={(c) => t(`HEARTBEAT_RHYTHM__${c}`)} + optionValue={(c) => c} value={log.rhythm} - onChange={(c) => - onChange({ rhythm: c.value as DailyRoundsModel["rhythm"] }) - } + onChange={(c) => onChange({ rhythm: c.value ?? undefined })} /> onChange({ rhythm_detail: c.value })} diff --git a/src/Components/Patient/models.tsx b/src/Components/Patient/models.tsx index 5387e457e9e..7676d294b4c 100644 --- a/src/Components/Patient/models.tsx +++ b/src/Components/Patient/models.tsx @@ -2,6 +2,7 @@ import { ConsultationModel, PatientCategory } from "../Facility/models"; import { PerformedByModel } from "../HCX/misc"; import { CONSCIOUSNESS_LEVEL, + HEARTBEAT_RHYTHM_CHOICES, HumanBodyRegion, INSULIN_INTAKE_FREQUENCY_OPTIONS, LIMB_RESPONSE_OPTIONS, @@ -12,7 +13,6 @@ import { PressureSoreTissueTypeOptions, RATION_CARD_CATEGORY, RESPIRATORY_SUPPORT, - RHYTHM_CHOICES, VENTILATOR_MODE_OPTIONS, } from "../../Common/constants"; @@ -311,7 +311,7 @@ export type IPressureSore = { }; export interface DailyRoundsModel { spo2?: number; - rhythm?: (typeof RHYTHM_CHOICES)[number]["text"]; + rhythm?: (typeof HEARTBEAT_RHYTHM_CHOICES)[number]; rhythm_detail?: string; bp?: BloodPressure; pulse?: number; diff --git a/src/Locale/en/Common.json b/src/Locale/en/Common.json index c5d6d76564e..8b421d58e8f 100644 --- a/src/Locale/en/Common.json +++ b/src/Locale/en/Common.json @@ -183,6 +183,7 @@ "GENDER__1": "Male", "GENDER__2": "Female", "GENDER__3": "Non-binary", + "normal": "Normal", "done": "Done", "view": "View", "rename": "Rename", diff --git a/src/Locale/en/LogUpdate.json b/src/Locale/en/LogUpdate.json index 319a87bd8d8..080e2fc979a 100644 --- a/src/Locale/en/LogUpdate.json +++ b/src/Locale/en/LogUpdate.json @@ -52,5 +52,22 @@ "NURSING_CARE_PROCEDURE__chest_tube_care": "Chest Tube Care", "NURSING_CARE_PROCEDURE__tracheostomy_care": "Tracheostomy Care", "NURSING_CARE_PROCEDURE__stoma_care": "Stoma Care", - "NURSING_CARE_PROCEDURE__catheter_care": "Catheter Care" + "NURSING_CARE_PROCEDURE__catheter_care": "Catheter Care", + "HEARTBEAT_RHYTHM__REGULAR": "Regular", + "HEARTBEAT_RHYTHM__IRREGULAR": "Irregular", + "HEARTBEAT_RHYTHM__UNKNOWN": "Unknown", + "heartbeat_rhythm": "Heartbeat Rhythm", + "heartbeat_description": "Heartbeat Description", + "blood_pressure": "Blood Pressure", + "map_acronym": "M.A.P.", + "systolic": "Systolic", + "diastolic": "Diastolic", + "temperature": "Temperature", + "resipiratory_rate": "Respiratory Rate", + "pain": "Pain", + "pain_chart_description": "Mark region and intensity of pain", + "pulse": "Pulse", + "bradycardia": "Bradycardia", + "tachycardia": "Tachycardia", + "spo2": "SpO₂" } \ No newline at end of file From 87b01238a83ed84abe6276e6998fb84932a7f53c Mon Sep 17 00:00:00 2001 From: Khavin Shankar Date: Tue, 3 Sep 2024 11:09:57 +0530 Subject: [PATCH 06/19] Improve Treament Summary Report (#8295) --- src/Components/Facility/TreatmentSummary.tsx | 761 ++++++++++++------- src/Locale/en/Common.json | 1 + src/Locale/en/Facility.json | 40 + src/Routers/routes/ConsultationRoutes.tsx | 7 +- 4 files changed, 520 insertions(+), 289 deletions(-) diff --git a/src/Components/Facility/TreatmentSummary.tsx b/src/Components/Facility/TreatmentSummary.tsx index e75eec5b80a..e719e859a4b 100644 --- a/src/Components/Facility/TreatmentSummary.tsx +++ b/src/Components/Facility/TreatmentSummary.tsx @@ -4,16 +4,34 @@ import { formatDateTime, formatPatientAge, } from "../../Utils/utils"; -import useSlug from "../../Common/hooks/useSlug"; import useAppHistory from "../../Common/hooks/useAppHistory"; import routes from "../../Redux/api"; import useQuery from "../../Utils/request/useQuery"; import CareIcon from "../../CAREUI/icons/CareIcon"; +import { ConsultationModel } from "./models"; +import { useMemo } from "react"; +import { + ActiveConditionVerificationStatuses, + ConsultationDiagnosis, +} from "../Diagnosis/types"; +import PageHeadTitle from "../Common/PageHeadTitle"; +import { useTranslation } from "react-i18next"; +import { PatientModel } from "../Patient/models"; +import MedicineRoutes from "../Medicine/routes"; + +export interface ITreatmentSummaryProps { + consultationId: string; + patientId: string; + facilityId: string; +} -const TreatmentSummary = (props: any) => { - const { consultationId, patientId } = props; +export default function TreatmentSummary({ + consultationId, + patientId, + facilityId, +}: ITreatmentSummaryProps) { + const { t } = useTranslation(); const date = new Date(); - const facilityId = useSlug("facility"); const { goBack } = useAppHistory(); const url = `/facility/${facilityId}/patient/${patientId}/consultation/${consultationId}`; @@ -22,11 +40,6 @@ const TreatmentSummary = (props: any) => { prefetch: patientId !== undefined, }); - const { data: investigations } = useQuery(routes.getInvestigation, { - pathParams: { consultation_external_id: consultationId }, - prefetch: consultationId !== undefined, - }); - const { data: consultationData } = useQuery(routes.getConsultation, { pathParams: { id: consultationId }, prefetch: consultationId !== undefined, @@ -34,16 +47,19 @@ const TreatmentSummary = (props: any) => { return (
+
@@ -52,290 +68,463 @@ const TreatmentSummary = (props: any) => { {consultationData?.facility_name ?? ""} -

INTERIM TREATMENT SUMMARY

+

+ {t("treatment_summary__heading")} +

{formatDate(date)}
-
-
-
- Name : {patientData?.name ?? ""} -
-
- Address : {patientData?.address ?? ""} -
-
- -
-
-
- Age :{" "} - {patientData ? formatPatientAge(patientData, true) : ""} -
-
- OP : {consultationData?.patient_no ?? ""} -
-
+
+ -
- {consultationData?.suggestion === "DC" ? ( - Date of domiciliary care commenced : - ) : ( - Date of admission : - )} - - {consultationData?.encounter_date - ? formatDateTime(consultationData.encounter_date) - : " --/--/----"} - -
-
+ -
-
- Gender : - {GENDER_TYPES.find((i) => i.id === patientData?.gender)?.text} -
+ -
- Contact person : - - {" "} - {patientData?.emergency_phone_number - ? patientData.emergency_phone_number - : " -"} - -
-
- -
- Comorbidities : -
- - - - - - - - - {patientData?.medical_history && - patientData.medical_history.length > 0 ? ( - patientData.medical_history.map( - (obj: any, index: number) => { - return ( - - - - - ); - }, - ) - ) : ( - - - - - )} - -
DiseaseDetails
- {obj["disease"]} - - {obj["details"] ? obj["details"] : "---"} -
- --- - - --- -
-
-
- -
- Diagnosis : -
-
- History of present illness : - {consultationData?.history_of_present_illness - ? consultationData.history_of_present_illness - : " ---"} -
- -
- Examination details and clinical conditions : - {consultationData?.examination_details - ? consultationData.examination_details - : " ---"} -
- -
- Physical Examination info : - {consultationData?.last_daily_round?.physical_examination_info - ? consultationData.last_daily_round - ?.physical_examination_info - : " ---"} -
-
-
- -
- General Instructions : - {patientData?.last_consultation?.consultation_notes ? ( -
- {patientData.last_consultation.consultation_notes} -
- ) : ( - " ---" - )} -
- -
- Relevant investigations : - -
- - - - - - - - - - - - - - {investigations && investigations.results.length > 0 ? ( - investigations.results.map( - (value: any, index: number) => { - return ( - - - - - - - - - ); - }, - ) - ) : ( - - - - - - - - - )} - -
- Date - - Name - - Result - - Ideal value - - values range - - unit -
- {formatDate( - value["session_object"][ - "session_created_date" - ], - )} - - {value["investigation_object"]["name"]} - - {value["notes"] || value["value"]} - - {value["investigation_object"]["ideal_value"] || - "-"} - - {value["investigation_object"]["min_value"]} -{" "} - {value["investigation_object"]["max_value"]} - - {value["investigation_object"]["unit"] || "-"} -
- --- - - --- - - --- - - --- - - --- - - --- -
-
-
- -
- Treatment : - {consultationData?.treatment_plan ? ( -

{consultationData.treatment_plan}

- ) : ( -

---

- )} - Treatment summary/Treament Plan : - -
- - - - - - - - - - - {consultationData?.last_daily_round ? ( - - - - - - ) : ( - - - - - - )} - -
DateSpo2 - Temperature -
- {formatDateTime( - consultationData.last_daily_round.modified_date, - )} - - {consultationData.last_daily_round.ventilator_spo2 || - "-"} - - {consultationData.last_daily_round.temperature || "-"} -
- --- - - --- - - --- -
-
-
+ + + + + + +
); -}; +} + +interface IBasicDetailsSection { + patientData?: PatientModel; + consultationData?: ConsultationModel; +} + +function BasicDetailsSection({ + patientData, + consultationData, +}: IBasicDetailsSection) { + const { t } = useTranslation(); + + return ( + <> +
+
+ {t("patient_registration__name")} : {patientData?.name ?? ""} +
+
+ {t("patient_registration__address")} :{" "} + {patientData?.address ?? ""} +
+
+ +
+
+
+ {t("patient_registration__age")} :{" "} + {patientData ? formatPatientAge(patientData, true) : ""} +
+
+ + {consultationData?.suggestion === "A" + ? t("patient_consultation__ip") + : t("patient_consultation__op")}{" "} + : + {" "} + {consultationData?.patient_no ?? ""} +
+
+ +
+ {consultationData?.suggestion === "DC" ? ( + {t("patient_consultation__dc_admission")} : + ) : ( + {t("patient_consultation__admission")} : + )}{" "} + + {consultationData?.encounter_date + ? formatDateTime(consultationData.encounter_date) + : t("empty_date_time")} + +
+
+ +
+
+ {t("patient_registration__gender")} :{" "} + {GENDER_TYPES.find((i) => i.id === patientData?.gender)?.text} +
+ +
+ {t("patient_registration__contact")} :{" "} + {patientData?.emergency_phone_number ?? ""} +
+
+ + ); +} + +interface IComorbiditiesSection { + patientData?: PatientModel; +} + +function ComorbiditiesSection({ patientData }: IComorbiditiesSection) { + const { t } = useTranslation(); + + return patientData?.medical_history?.filter( + (comorbidities) => comorbidities.disease !== "NO", + ).length ? ( +
+ {t("patient_registration__comorbidities")} +
+ + + + + + + + + {patientData.medical_history.map((obj, index) => { + return ( + + + + + ); + })} + +
+ {t("patient_registration__comorbidities__disease")} + + {t("patient_registration__comorbidities__details")} +
+ {obj["disease"]} + + {obj["details"] || "---"} +
+
+
+ ) : null; +} + +interface IDiagnosisSection { + consultationData?: ConsultationModel; +} + +type DiagnosisType = + | (typeof ActiveConditionVerificationStatuses)[number] + | "principal"; + +function DiagnosisSection({ consultationData }: IDiagnosisSection) { + const { t } = useTranslation(); + + const diagnoses = useMemo(() => { + return consultationData?.diagnoses?.reduce( + (acc, curr) => { + if (curr.is_principal) { + acc.principal.push(curr); + } else if ( + ActiveConditionVerificationStatuses.includes( + curr.verification_status as (typeof ActiveConditionVerificationStatuses)[number], + ) + ) { + acc[curr.verification_status as keyof typeof acc].push(curr); + } + + return acc; + }, + { + principal: [], + confirmed: [], + provisional: [], + unconfirmed: [], + differential: [], + } as Record, + ); + }, [consultationData?.diagnoses]); + + if (!diagnoses) { + return null; + } + + return ( +
+ {t("diagnosis")} +
+ {( + [ + "principal", + "confirmed", + "provisional", + "unconfirmed", + "differential", + ] as DiagnosisType[] + ).map( + (type) => + !!diagnoses[type].length && ( +
+ + {t(`diagnosis__${type}`)} {t("diagnosis")} + +
    + {diagnoses[type].map((d) => ( +
  1. + {d.diagnosis_object.label} + {d.is_principal && ( + + {t(`diagnosis__${d.verification_status}`)} + + )} +
  2. + ))} +
+
+ ), + )} +
+
+ ); +} + +interface IInvestigationsSection { + consultationId: string; +} + +function InvestigationsSection({ consultationId }: IInvestigationsSection) { + const { t } = useTranslation(); + + const { data: investigations } = useQuery(routes.getInvestigation, { + pathParams: { consultation_external_id: consultationId }, + prefetch: consultationId !== undefined, + }); + + return investigations?.results.length ? ( +
+ {t("suggested_investigations")} + +
+ + + + + + + + + + + + + + {investigations?.results.map((value, index) => ( + + + + + + + + + ))} + +
+ {t("investigations__date")} + + {t("investigations__name")} + + {t("investigations__result")} + + {t("investigations__ideal_value")} + + {t("investigations__range")} + + {t("investigations__unit")} +
+ {formatDate(value["session_object"]["session_created_date"])} + + {value["investigation_object"]["name"]} + + {value["notes"] || value["value"]} + + {value["investigation_object"]["ideal_value"] || "-"} + + {value["investigation_object"]["min_value"]} -{" "} + {value["investigation_object"]["max_value"]} + + {value["investigation_object"]["unit"] || "-"} +
+
+
+ ) : null; +} + +interface ITreatmentSection { + consultationData?: ConsultationModel; +} + +function TreatmentSection({ consultationData }: ITreatmentSection) { + const { t } = useTranslation(); + + const isTreatmentSummaryAvailable = useMemo(() => { + return ( + consultationData?.last_daily_round && + (consultationData.last_daily_round.ventilator_spo2 || + consultationData.last_daily_round.temperature) + ); + }, [consultationData]); -export default TreatmentSummary; + return consultationData?.treatment_plan || isTreatmentSummaryAvailable ? ( +
+ {consultationData?.treatment_plan && ( + <> + {t("patient_consultation__treatment__plan")} +

{consultationData.treatment_plan}

+ + )} + + {isTreatmentSummaryAvailable && ( + <> + + {t("patient_consultation__treatment__summary")} + +
+ + + + + + + + + + + + + + + + +
+ {t("patient_consultation__treatment__summary__date")} + + {t("patient_consultation__treatment__summary__spo2")} + + {t("patient_consultation__treatment__summary__temperature")} +
+ {formatDateTime( + consultationData?.last_daily_round?.modified_date, + )} + + {consultationData?.last_daily_round?.ventilator_spo2 || "-"} + + {consultationData?.last_daily_round?.temperature || "-"} +
+
+ + )} +
+ ) : null; +} + +interface IPrescriptionsSection { + consultationId: string; +} + +function PrescriptionsSection({ consultationId }: IPrescriptionsSection) { + const { t } = useTranslation(); + + const { data: prescriptions } = useQuery(MedicineRoutes.listPrescriptions, { + pathParams: { consultation: consultationId }, + query: { discontinued: false }, + }); + + return prescriptions?.results.length ? ( +
+ {t("active_prescriptions")} + +
+ + + + + + + + + + + + {prescriptions?.results.map((prescription, index) => ( + + + + + + + ))} + +
+ {t("prescriptions__medicine")} + + {t("prescriptions__route")} + + {t("prescriptions__dosage_frequency")} + + {t("prescriptions__start_date")} +
+ {prescription.medicine_object?.name ?? "-"} + + {prescription.route ?? "-"} + + {prescription.dosage_type !== "TITRATED" ? ( +

{prescription.base_dosage}

+ ) : ( +

+ {prescription.base_dosage} - {prescription.target_dosage} +

+ )} + +

+ {prescription.dosage_type !== "PRN" + ? t("PRESCRIPTION_FREQUENCY_" + prescription.frequency) + : prescription.indicator} +

+
+ {formatDate(prescription.created_date)} +
+
+
+ ) : null; +} + +interface IInstructionsSection { + consultationData?: ConsultationModel; +} + +function InstructionsSection({ consultationData }: IInstructionsSection) { + const { t } = useTranslation(); + + return ( + <> + {consultationData?.consultation_notes && ( +
+ {t("patient_consultation__consultation_notes")} + +
{consultationData.consultation_notes}
+
+ )} + + {consultationData?.special_instruction && ( +
+ {t("patient_consultation__special_instruction")} + +
{consultationData.special_instruction}
+
+ )} + + ); +} diff --git a/src/Locale/en/Common.json b/src/Locale/en/Common.json index 8b421d58e8f..88fc42391bc 100644 --- a/src/Locale/en/Common.json +++ b/src/Locale/en/Common.json @@ -176,6 +176,7 @@ "ration_card__NO_CARD": "Non-card holder", "ration_card__BPL": "BPL", "ration_card__APL": "APL", + "empty_date_time": "--:-- --; --/--/----", "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.", diff --git a/src/Locale/en/Facility.json b/src/Locale/en/Facility.json index 16bc6610631..856faca4384 100644 --- a/src/Locale/en/Facility.json +++ b/src/Locale/en/Facility.json @@ -55,6 +55,46 @@ "discharged_patients": "Discharged Patients", "discharged_patients_empty": "No discharged patients present in this facility", "update_facility_middleware_success": "Facility middleware updated successfully", + "treatment_summary__head_title": "Treatment Summary", + "treatment_summary__print": "Print Treatment Summary", + "treatment_summary__heading": "INTERIM TREATMENT SUMMARY", + "patient_registration__name": "Name", + "patient_registration__address": "Address", + "patient_registration__age": "Age", + "patient_consultation__op": "OP", + "patient_consultation__ip": "IP", + "patient_consultation__dc_admission": "Date of domiciliary care commenced", + "patient_consultation__admission": "Date of admission", + "patient_registration__gender": "Gender", + "patient_registration__contact": "Emergency Contact", + "patient_registration__comorbidities": "Comorbidities", + "patient_registration__comorbidities__disease": "Disease", + "patient_registration__comorbidities__details": "Details", + "patient_consultation__consultation_notes": "General Instructions", + "patient_consultation__special_instruction": "Special Instructions", + "suggested_investigations": "Suggested Investigations", + "investigations__date": "Date", + "investigations__name": "Name", + "investigations__result": "Result", + "investigations__ideal_value": "Ideal Value", + "investigations__range": "Value Range", + "investigations__unit": "Unit", + "patient_consultation__treatment__plan": "Plan", + "patient_consultation__treatment__summary": "Summary", + "patient_consultation__treatment__summary__date": "Date", + "patient_consultation__treatment__summary__spo2": "SpO2", + "patient_consultation__treatment__summary__temperature": "Temperature", + "diagnosis": "Diagnosis", + "diagnosis__principal": "Principal", + "diagnosis__confirmed": "Confirmed", + "diagnosis__provisional": "Provisional", + "diagnosis__unconfirmed": "Unconfirmed", + "diagnosis__differential": "Differential", + "active_prescriptions": "Active Prescriptions", + "prescriptions__medicine": "Medicine", + "prescriptions__route": "Route", + "prescriptions__dosage_frequency": "Dosage & Frequency", + "prescriptions__start_date": "Prescribed On", "select_facility_for_discharged_patients_warning": "Facility needs to be selected to view discharged patients.", "duplicate_patient_record_confirmation": "Admit the patient record to your facility by adding the year of birth", "duplicate_patient_record_rejection": "I confirm that the suspect / patient I want to create is not on the list.", diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index 9209b50e97e..598fd06adc3 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -5,7 +5,9 @@ import ManagePrescriptions from "../../Components/Medicine/ManagePrescriptions"; import { DailyRoundListDetails } from "../../Components/Patient/DailyRoundListDetails"; import { DailyRounds } from "../../Components/Patient/DailyRounds"; import { ConsultationDetails } from "../../Components/Facility/ConsultationDetails"; -import TreatmentSummary from "../../Components/Facility/TreatmentSummary"; +import TreatmentSummary, { + ITreatmentSummaryProps, +} from "../../Components/Facility/TreatmentSummary"; import ConsultationDoctorNotes from "../../Components/Facility/ConsultationDoctorNotes"; import PatientConsentRecords from "../../Components/Patient/PatientConsentRecords"; import CriticalCareEditor from "../../Components/LogUpdate/CriticalCareEditor"; @@ -129,12 +131,11 @@ export default { ), "/facility/:facilityId/patient/:patientId/consultation/:consultationId/treatment-summary": - ({ facilityId, patientId, consultationId }: any) => ( + ({ facilityId, patientId, consultationId }: ITreatmentSummaryProps) => ( ), "/facility/:facilityId/patient/:patientId/consultation/:consultationId/notes": From c47133200fcdb63458d722c5b0618660d1b50e81 Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Tue, 3 Sep 2024 15:03:48 +0530 Subject: [PATCH 07/19] Navigate to session expired page instead of showing notification (#8363) --- src/Utils/request/handleResponse.ts | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/Utils/request/handleResponse.ts b/src/Utils/request/handleResponse.ts index 9f85b50a68d..905417ad0e2 100644 --- a/src/Utils/request/handleResponse.ts +++ b/src/Utils/request/handleResponse.ts @@ -20,14 +20,15 @@ export default function handleResponse( // Other Errors between 400-599 (inclusive) if (res.status >= 400 && res.status < 600) { - // Invalid token - if (!silent && error?.code === "token_not_valid") { - navigate(`/session-expired?redirect=${window.location.href}`); - } - - // Handle session expiry - if (error?.detail === "Authentication credentials were not provided.") { - notify?.Error({ msg: "Session expired. Please Login again." }); + // Handle invalid token / session expiry + if ( + !silent && + (error?.code === "token_not_valid" || + error?.detail === "Authentication credentials were not provided.") + ) { + if (!location.pathname.startsWith("/session-expired")) { + navigate(`/session-expired?redirect=${window.location.href}`); + } return; } From c1614908cc8e079f296fb09cfef203a6c8919663 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 3 Sep 2024 15:05:22 +0530 Subject: [PATCH 08/19] Removes auto fill for uploaded file name (#8464) --- src/Utils/useFileUpload.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Utils/useFileUpload.tsx b/src/Utils/useFileUpload.tsx index ad8e185745c..fdd69c45765 100644 --- a/src/Utils/useFileUpload.tsx +++ b/src/Utils/useFileUpload.tsx @@ -84,11 +84,13 @@ export default function useFileUpload( const f = e.target.files[0]; const fileName = f.name; setFile(e.target.files[0]); - setUploadFileName( - uploadFileName || - fileName.substring(0, fileName.lastIndexOf(".")) || - fileName, - ); + + // This is commented out to prompt users to input valid file names. See https://github.com/ohcnetwork/care_fe/issues/7942#issuecomment-2324391329 + //setUploadFileName( + // uploadFileName || + // fileName.substring(0, fileName.lastIndexOf(".")) || + // fileName, + //); const ext: string = fileName.split(".")[1]; From 5656d65c0963bb4dc2882a08d1edd4f4cdf31046 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 3 Sep 2024 15:08:18 +0530 Subject: [PATCH 09/19] Added translations for home page (#8461) Co-authored-by: Rithvik Nishad --- .../e2e/facility_spec/facility_creation.cy.ts | 2 +- src/Components/Facility/FacilityHome.tsx | 57 ++++++++++--------- src/Locale/en/Common.json | 8 ++- src/Locale/en/Facility.json | 19 ++++++- 4 files changed, 54 insertions(+), 32 deletions(-) diff --git a/cypress/e2e/facility_spec/facility_creation.cy.ts b/cypress/e2e/facility_spec/facility_creation.cy.ts index a092d23390e..ef309af2c24 100644 --- a/cypress/e2e/facility_spec/facility_creation.cy.ts +++ b/cypress/e2e/facility_spec/facility_creation.cy.ts @@ -37,7 +37,7 @@ describe("Facility Creation", () => { const totalOccupancy = "10"; const doctorCapacity = "5"; const totalDoctor = "10"; - const facilityName = "cypress facility"; + const facilityName = "Cypress Facility"; const facilityName2 = "Dummy Facility 40"; const facilityAddress = "cypress address"; const facilityUpdateAddress = "cypress updated address"; diff --git a/src/Components/Facility/FacilityHome.tsx b/src/Components/Facility/FacilityHome.tsx index 50edef824c8..a0dcf06026f 100644 --- a/src/Components/Facility/FacilityHome.tsx +++ b/src/Components/Facility/FacilityHome.tsx @@ -88,7 +88,7 @@ export const FacilityHome = ({ facilityId }: Props) => { onResponse: ({ res }) => { if (res?.ok) { Notification.Success({ - msg: "Facility deleted successfully", + msg: t("deleted_successfully", { name: facilityData?.name }), }); } navigate("/facility"); @@ -150,11 +150,10 @@ export const FacilityHome = ({ facilityId }: Props) => { backUrl="/facility" > - Are you sure you want to delete{" "} - {facilityData?.name} + {t("are_you_sure_want_to_delete", { name: facilityData?.name })} } action="Delete" @@ -247,7 +246,7 @@ export const FacilityHome = ({ facilityId }: Props) => {

- Address + {t("address")}

{facilityData?.address} @@ -257,7 +256,7 @@ export const FacilityHome = ({ facilityId }: Props) => {

- Local Body + {t("local_body")}

{facilityData?.local_body_object?.name} @@ -266,7 +265,7 @@ export const FacilityHome = ({ facilityId }: Props) => {

- Ward + {t("ward")}

{facilityData?.ward_object?.number + @@ -276,7 +275,7 @@ export const FacilityHome = ({ facilityId }: Props) => {

- District + {t("district")}

{facilityData?.district_object?.name} @@ -286,7 +285,7 @@ export const FacilityHome = ({ facilityId }: Props) => {

- Phone Number + {t("phone_number")}

{ {facilityData?.features?.some((feature) => FACILITY_FEATURE_TYPES.some((f) => f.id === feature), ) && ( -

Available features

+

+ {t("available_features")} +

)}
{ authorizeFor={NonReadOnlyUsers} icon={} > - Update Facility + {t("update_facility")} { authorizeFor={NonReadOnlyUsers} icon={} > - Configure Facility + {t("configure_facility")} navigate(`/facility/${facilityId}/inventory`)} icon={} > - Inventory Management + {t("inventory_management")} { } > - Location Management + {t("location_management")} { authorizeFor={NonReadOnlyUsers} icon={} > - Resource Request + {t("resource_request")} { authorizeFor={NonReadOnlyUsers} icon={} > - Create Asset + {t("create_asset")} navigate(`/assets?facility=${facilityId}`)} icon={} > - View Assets + {t("view_asset")} navigate(`/facility/${facilityId}/users`)} icon={} > - View Users + {t("view_users")} navigate(`/facility/${facilityId}/abdm`)} icon={} > - View ABDM Records + {t("view_abdm_records")} {hasPermissionToDeleteFacility ? ( { className="flex items-center gap-3" icon={} > - Delete Facility + {t("delete_facility")} ) : ( <> @@ -437,7 +438,7 @@ export const FacilityHome = ({ facilityId }: Props) => { onClick={() => navigate(`/facility/${facilityId}/cns`)} > - Central Nursing Station + {t("central_nursing_station")} {CameraFeedPermittedUserTypes.includes(authUser.user_type) && ( @@ -451,7 +452,7 @@ export const FacilityHome = ({ facilityId }: Props) => { authorizeFor={NonReadOnlyUsers} > - Add Details of a Patient + {t("add_details_of_patient")} { onClick={() => navigate(`/patients?facility=${facilityId}`)} > - View Patients + {t("view_patients")}
@@ -472,7 +473,7 @@ export const FacilityHome = ({ facilityId }: Props) => {
-

Oxygen Information

+

{t("oxygen_information")}

{ const facilityId = useSlug("facility"); const [location, setLocation] = useState(); + const { t } = useTranslation(); + return ( @@ -528,7 +531,7 @@ const LiveMonitoringButton = () => { id="facility-detailspage-livemonitoring" > - Live Monitoring + {t("live_monitoring")} @@ -545,7 +548,7 @@ const LiveMonitoringButton = () => {
- Choose a location + {t("choose_location")}
{ className="w-full" href={`/facility/${facilityId}/live-monitoring?location=${location}`} > - Open Live Monitoring + {t("open_live_monitoring")}
diff --git a/src/Locale/en/Common.json b/src/Locale/en/Common.json index 88fc42391bc..085442963a0 100644 --- a/src/Locale/en/Common.json +++ b/src/Locale/en/Common.json @@ -194,5 +194,9 @@ "live": "Live", "discharged": "Discharged", "archived": "Archived", - "no_changes_made": "No changes made" -} \ No newline at end of file + "no_changes_made": "No changes made", + "are_you_sure_want_to_delete": "Are you sure you want to delete {{name}}?", + "oxygen_information": "Oxygen Information", + "deleted_successfully": "{{name}} deleted successfully", + "delete_item": "Delete {{name}}" +} diff --git a/src/Locale/en/Facility.json b/src/Locale/en/Facility.json index 856faca4384..d19c889a98f 100644 --- a/src/Locale/en/Facility.json +++ b/src/Locale/en/Facility.json @@ -100,5 +100,20 @@ "duplicate_patient_record_rejection": "I confirm that the suspect / patient I want to create is not on the list.", "duplicate_patient_record_birth_unknown": "Please contact your district care coordinator, the shifting facility or the patient themselves if you are not sure about the patient's year of birth.", "patient_transfer_birth_match_note": "Note: Year of birth must match the patient to process the transfer request.", - "cover_image_updated_note": "It could take a while to see the updated cover image" -} \ No newline at end of file + "cover_image_updated_note": "It could take a while to see the updated cover image", + "available_features": "Available Features", + "update_facility": "Update Facility", + "configure_facility": "Configure Facility", + "inventory_management": "Inventory Management", + "location_management": "Location Management", + "resource_request": "Resource Request", + "view_asset": "View Assets", + "view_users": "View Users", + "view_abdm_records": "View ABDM Records", + "delete_facility": "Delete Facility", + "central_nursing_station": "Central Nursing Station", + "add_details_of_patient": "Add Details of Patient", + "choose_location": "Choose Location", + "live_monitoring": "Live Monitoring", + "open_live_monitoring": "Open Live Monitoring" +} From c1e5f329c820e3d4807fc6d57025f774bdff96d7 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Tue, 3 Sep 2024 15:24:51 +0530 Subject: [PATCH 10/19] Fixed User page Spacing and added translations (#8462) Co-authored-by: Khavin Shankar --- src/Components/Facility/FacilityUsers.tsx | 22 ++++++++++------------ src/Locale/en/Common.json | 2 ++ src/Locale/en/Users.json | 6 ++++-- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/Components/Facility/FacilityUsers.tsx b/src/Components/Facility/FacilityUsers.tsx index 61231ed7d21..2572731f720 100644 --- a/src/Components/Facility/FacilityUsers.tsx +++ b/src/Components/Facility/FacilityUsers.tsx @@ -27,7 +27,6 @@ export default function FacilityUsers(props: any) { const [currentPage, setCurrentPage] = useState(1); const [expandFacilityList, setExpandFacilityList] = useState(false); const [selectedUser, setSelectedUser] = useState(null); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [offset, setOffset] = useState(0); const [linkFacility, setLinkFacility] = useState<{ @@ -103,7 +102,7 @@ export default function FacilityUsers(props: any) { onResponse: ({ res }) => { if (res?.status === 204) { Notification.Success({ - msg: "User deleted successfully", + msg: t("user_deleted_successfuly"), }); } }, @@ -152,7 +151,7 @@ export default function FacilityUsers(props: any) {
)}
- Last Online:{" "} + {t("last_online")}{" "} {user.last_login ? relativeTime(user.last_login) - : "Never"} + : t("never")}
@@ -189,7 +188,7 @@ export default function FacilityUsers(props: any) {
- Phone: + {t("phone_number")}
-

Linked Facilities

+

{t("linked_facilities")}

)} @@ -250,7 +249,7 @@ export default function FacilityUsers(props: any) { manageUsers = (
-
No Users Found
+
{t("no_users_found")}
); @@ -258,9 +257,8 @@ export default function FacilityUsers(props: any) { return ( {linkFacility.show && ( @@ -271,10 +269,10 @@ export default function FacilityUsers(props: any) { /> )} -
+
{facilityUserData && ( -
+
{manageUsers}
{userData.show && ( diff --git a/src/Locale/en/Common.json b/src/Locale/en/Common.json index 085442963a0..24855e05d70 100644 --- a/src/Locale/en/Common.json +++ b/src/Locale/en/Common.json @@ -195,6 +195,8 @@ "discharged": "Discharged", "archived": "Archived", "no_changes_made": "No changes made", + "user_deleted_successfuly": "User Deleted Successfuly", + "users": "Users", "are_you_sure_want_to_delete": "Are you sure you want to delete {{name}}?", "oxygen_information": "Oxygen Information", "deleted_successfully": "{{name}} deleted successfully", diff --git a/src/Locale/en/Users.json b/src/Locale/en/Users.json index 01fe031c61a..035ed8933a5 100644 --- a/src/Locale/en/Users.json +++ b/src/Locale/en/Users.json @@ -4,11 +4,13 @@ "add_new_user": "Add New User", "no_users_found": "No Users Found", "home_facility": "Home Facility", - "no_home_facility" : "No home facility assigned", + "no_home_facility": "No home facility assigned", "clear_home_facility": "Clear Home Facility", "linked_facilities": "Linked Facilities", "no_linked_facilities": "No Linked Facilities", "average_weekly_working_hours": "Average weekly working hours", "set_average_weekly_working_hours_for": "Set Average weekly working hours for", - "search_by_username": "Search by username" + "search_by_username": "Search by username", + "last_online": "Last Online", + "total_users": "Total Users" } From 5e95d60c9d8cb79662f887cd962d372eab817f30 Mon Sep 17 00:00:00 2001 From: Shivank Kacker Date: Wed, 4 Sep 2024 00:16:41 +0530 Subject: [PATCH 11/19] Add suggestion chips instead of autofilling diagnosis (#8369) * Add suggestion chips instead of autofilling diagnosis * shortfix * fixed onclick * Fix bug with chip click, and hid already entered diagnoses * revert hiding already entered diagnosis * added onselect to AddICD11DiagnosisProps --- .../AddICD11Diagnosis.tsx | 9 +- .../ConsultationDiagnosisBuilder.tsx | 24 ++++- src/Components/Patient/DailyRounds.tsx | 89 +++---------------- src/Components/Scribe/formDetails.ts | 6 +- 4 files changed, 48 insertions(+), 80 deletions(-) diff --git a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx index 66e0592511c..6bc309f5716 100644 --- a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx +++ b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/AddICD11Diagnosis.tsx @@ -17,6 +17,8 @@ interface AddICD11DiagnosisProps { onAdd: (object: CreateDiagnosis) => Promise; disallowed: ICD11DiagnosisModel[]; disabled?: boolean; + prefill?: ICD11DiagnosisModel; + onSelect?: (selected: ICD11DiagnosisModel) => unknown; } export default function AddICD11Diagnosis(props: AddICD11DiagnosisProps) { @@ -36,6 +38,8 @@ export default function AddICD11Diagnosis(props: AddICD11DiagnosisProps) { } }, [res?.status]); + useEffect(() => props.prefill && setSelected(props.prefill), [props.prefill]); + const handleAdd = async (status: CreateDiagnosis["verification_status"]) => { if (!selected) return; @@ -64,7 +68,10 @@ export default function AddICD11Diagnosis(props: AddICD11DiagnosisProps) { disabled={props.disabled || adding} placeholder={t("search_icd11_placeholder")} value={selected} - onChange={(e) => setSelected(e.value)} + onChange={(e) => { + setSelected(e.value); + props.onSelect?.(e.value); + }} options={mergeQueryOptions( selected ? [selected] : [], data ?? [], diff --git a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx index b6495143f5d..043654929a8 100644 --- a/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx +++ b/src/Components/Diagnosis/ConsultationDiagnosisBuilder/ConsultationDiagnosisBuilder.tsx @@ -11,6 +11,7 @@ import request from "../../../Utils/request/request"; import DiagnosesRoutes from "../routes"; import * as Notification from "../../../Utils/Notifications"; import PrincipalDiagnosisSelect from "./PrincipalDiagnosisSelect"; +import CareIcon from "../../../CAREUI/icons/CareIcon"; interface CreateDiagnosesProps { className?: string; @@ -78,11 +79,14 @@ export const CreateDiagnosesBuilder = (props: CreateDiagnosesProps) => { interface EditDiagnosesProps { className?: string; value: ConsultationDiagnosis[]; + suggestions?: ICD11DiagnosisModel[]; + onUpdate?: (diagnoses: ConsultationDiagnosis[]) => void; } export const EditDiagnosesBuilder = (props: EditDiagnosesProps) => { const consultation = useSlug("consultation"); const [diagnoses, setDiagnoses] = useState(props.value); + const [prefill, setPrefill] = useState(); useEffect(() => { setDiagnoses(props.value); @@ -129,16 +133,34 @@ export const EditDiagnosesBuilder = (props: EditDiagnosesProps) => { if (res?.ok && data) { setDiagnoses([...diagnoses, data]); + setPrefill(undefined); + props.onUpdate?.(diagnoses); return true; } if (error) { Notification.Error({ msg: error }); } - return false; }} + prefill={prefill} + onSelect={() => setPrefill(undefined)} /> + {!!props.suggestions?.length && ( +
+ {props.suggestions?.map((suggestion, i) => ( + + ))} +
+ )}
diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index 8c09db3f8c2..81177925f08 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -46,9 +46,8 @@ 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"; +import { ICD11DiagnosisModel } from "../Facility/models"; const Loading = lazy(() => import("../Common/Loading")); @@ -58,7 +57,9 @@ export const DailyRounds = (props: any) => { const { goBack } = useAppHistory(); const { facilityId, patientId, consultationId, id } = props; const [symptomsSeed, setSymptomsSeed] = useState(1); - const [prescriptionSeed, setPrescriptionSeed] = useState(1); + const [diagnosisSuggestions, setDiagnosisSuggestions] = useState< + ICD11DiagnosisModel[] + >([]); const initForm: any = { physical_examination_info: "", @@ -489,6 +490,7 @@ export const DailyRounds = (props: any) => { { + setDiagnosisSuggestions([]); // Symptoms let rounds_type = fields.rounds_type || state.form.rounds_type; if (fields.additional_symptoms) { @@ -522,74 +524,10 @@ export const DailyRounds = (props: any) => { continue; } - const availableDiagnosis = icdData?.[0]?.id; + const availableDiagnosis = icdData?.slice(0, 5); - 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 (availableDiagnosis?.length) + setDiagnosisSuggestions(availableDiagnosis); } } @@ -598,8 +536,7 @@ export const DailyRounds = (props: any) => { [ "investigations", "icd11_diagnosis", - "prescriptions", - "prn_prescriptions", + "additional_symptoms", ].includes(f), ) && roundTypes.some((t) => t.id === "DOCTORS_LOG") @@ -850,7 +787,11 @@ export const DailyRounds = (props: any) => { {/* */} {diagnoses ? ( - + setDiagnosisSuggestions([])} + /> ) : (
Fetching existing diagnosis of patient... @@ -891,7 +832,6 @@ export const DailyRounds = (props: any) => { discontinued={ showDiscontinuedPrescriptions ? undefined : false } - key={prescriptionSeed} actions={["discontinue"]} />
@@ -916,7 +856,6 @@ export const DailyRounds = (props: any) => { showDiscontinuedPrescriptions ? undefined : false } actions={["discontinue"]} - key={prescriptionSeed} />
diff --git a/src/Components/Scribe/formDetails.ts b/src/Components/Scribe/formDetails.ts index 736ab971744..6300ac7e3c2 100644 --- a/src/Components/Scribe/formDetails.ts +++ b/src/Components/Scribe/formDetails.ts @@ -270,7 +270,7 @@ const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ }); return true; }, - }, + } /* { friendlyName: "Prescriptions", id: "prescriptions", @@ -330,7 +330,7 @@ const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ return true; }, }, - /*{ + { friendlyName: "Round Type", id: "rounds_type", type: "string", @@ -355,7 +355,7 @@ const DAILY_ROUND_FORM_SCRIBE_DATA: Field[] = [ "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: { [key: string]: ScribeForm } = { From 28b02d93fbf5cd8df9af3b164cfaf2e57b0c1023 Mon Sep 17 00:00:00 2001 From: Bodhish Thomas Date: Wed, 4 Sep 2024 00:28:32 +0530 Subject: [PATCH 12/19] Remove proxy and clean up config (#8445) Co-authored-by: Aakash Singh Co-authored-by: Rithvik Nishad --- .env | 6 +- .envrc | 1 + .example.env | 73 ++++++++++++ .github/workflows/cypress.yaml | 7 +- README.md | 4 +- care.config.ts | 103 +++++++++++++++++ cypress.config.ts | 3 + cypress/support/commands.ts | 8 +- index.html | 2 +- netlify.toml | 6 - src/App.tsx | 21 ++-- src/Common/constants.tsx | 53 +++++---- src/Common/hooks/useConfig.ts | 104 ------------------ src/Common/hooks/useFilters.tsx | 8 +- src/Components/Assets/AssetImportModal.tsx | 22 ++-- src/Components/Auth/Login.tsx | 45 ++++---- src/Components/Common/GLocationPicker.tsx | 5 +- src/Components/Common/Sidebar/Sidebar.tsx | 12 +- .../ExternalResultImportModal.tsx | 5 +- src/Components/Facility/BedCapacity.tsx | 17 ++- src/Components/Facility/ConsultationCard.tsx | 7 +- src/Components/Facility/ConsultationForm.tsx | 26 ++--- .../Facility/CoverImageEditModal.tsx | 4 +- src/Components/Facility/DischargeModal.tsx | 5 +- .../Facility/FacilityBedCapacity.tsx | 6 +- src/Components/Facility/FacilityCard.tsx | 5 +- src/Components/Facility/FacilityCreate.tsx | 16 +-- .../HCX/InsuranceDetailsBuilder.tsx | 5 +- src/Components/Medicine/PrintPreview.tsx | 9 +- src/Components/Patient/PatientFilter.tsx | 13 ++- src/Components/Patient/PatientInfoCard.tsx | 8 +- src/Components/Patient/PatientRegister.tsx | 14 ++- src/Components/Patient/ShiftCreate.tsx | 11 +- src/Components/Scribe/Scribe.tsx | 5 +- src/Components/Shifting/BoardView.tsx | 7 +- src/Components/Shifting/ListFilter.tsx | 13 ++- src/Components/Shifting/ListView.tsx | 8 +- src/Components/Shifting/ShiftDetails.tsx | 25 ++--- .../Shifting/ShiftDetailsUpdate.tsx | 21 ++-- src/Components/Shifting/ShiftingBoard.tsx | 6 +- src/Integrations/Plausible.tsx | 8 +- src/Integrations/Sentry.tsx | 13 +-- src/Providers/AppConfigProvider.tsx | 25 ----- src/Providers/AuthUserProvider.tsx | 12 +- src/Providers/index.tsx | 5 - src/Redux/Reducer.tsx | 22 +--- src/Redux/api.tsx | 8 -- src/Redux/fireRequest.tsx | 2 + src/Routers/AppRouter.tsx | 7 +- src/Utils/request/request.ts | 3 +- src/vite-env.d.ts | 35 +++++- tsconfig.json | 11 +- vite.config.mts | 17 +-- 53 files changed, 442 insertions(+), 445 deletions(-) create mode 100644 .example.env create mode 100644 care.config.ts delete mode 100644 src/Common/hooks/useConfig.ts delete mode 100644 src/Providers/AppConfigProvider.tsx delete mode 100644 src/Providers/index.tsx diff --git a/.env b/.env index 0496daa2587..8b3b02b3d97 100644 --- a/.env +++ b/.env @@ -1,12 +1,14 @@ # Whitelabelling envs REACT_APP_TITLE=CARE -REACT_APP_META_DESCRIPTION=Revolutionizing EMR with AI: Open Healthcare Network develops free, open-source tools to enhance efficiency in global healthcare delivery. Our EMR system is recognized as a Digital Public Good by the United Nations. +REACT_APP_META_DESCRIPTION="Revolutionizing EMR with AI: Open Healthcare Network develops free, open-source tools to enhance efficiency in global healthcare delivery. Our EMR system is recognized as a Digital Public Good by the United Nations." REACT_APP_COVER_IMAGE=https://cdn.ohc.network/care_logo.svg REACT_APP_COVER_IMAGE_ALT=https://cdn.ohc.network/care_logo.svg REACT_PUBLIC_URL=https://care.ohc.network +# Care API URL without the /api prefix +REACT_CARE_API_URL=https://careapi.ohc.network + # Dev envs ESLINT_NO_DEV_ERRORS=true - CARE_CDN_URL="https://egov-s3-facility-10bedicu.s3.amazonaws.com https://egov-s3-patient-data-10bedicu.s3.amazonaws.com http://localhost:4566" diff --git a/.envrc b/.envrc index fc7d890f90a..c729eb59836 100644 --- a/.envrc +++ b/.envrc @@ -1 +1,2 @@ +dotenv test -f .env.local && dotenv .env.local \ No newline at end of file diff --git a/.example.env b/.example.env new file mode 100644 index 00000000000..79332e6b9af --- /dev/null +++ b/.example.env @@ -0,0 +1,73 @@ +# Backend URL +REACT_CARE_API_URL=https://careapi.ohc.network + +# Dashboard URL +REACT_DASHBOARD_URL= + +# GitHub URL (default: https://github.com/ohcnetwork) +REACT_GITHUB_URL= + +# OHCN URL (default: https://ohc.network?ref=care) +REACT_OHCN_URL= + +# Plausible site domain (default: care.ohc.network) +REACT_PLAUSIBLE_SITE_DOMAIN= + +# Plausible server URL (default: https://plausible.10bedicu.in) +REACT_PLAUSIBLE_SERVER_URL= + + +# Main logo (JSON string with light and dark properties) +REACT_HEADER_LOGO= + +# Main logo (JSON string with light and dark properties) +# Example: REACT_MAIN_LOGO="{\"light\": \"https://cdn.ohc.network/light-logo.svg\", \"dark\": \"https://cdn.ohc.network/dark-logo.svg\"}" +REACT_MAIN_LOGO= + +# State logo (JSON string with light and dark properties) +REACT_STATE_LOGO= + +# Custom logo (JSON string with light and dark properties) +REACT_CUSTOM_LOGO= + +# Custom alternative logo (JSON string with light and dark properties) +REACT_CUSTOM_LOGO_ALT= + +# Custom description +REACT_CUSTOM_DESCRIPTION= + +# Google Maps API key +REACT_GMAPS_API_KEY= + +# Government data API key +REACT_GOV_DATA_API_KEY= + +# reCAPTCHA site key +REACT_RECAPTCHA_SITE_KEY= + +# Sentry DSN +REACT_SENTRY_DSN= + +# Sentry environment (default: staging) +REACT_SENTRY_ENVIRONMENT= + +# KASP settings +REACT_KASP_ENABLED=true +REACT_KASP_STRING=KASP +REACT_KASP_FULL_STRING=Karunya Arogya Suraksha Padhathi + +# Sample format file paths +REACT_SAMPLE_FORMAT_ASSET_IMPORT=/asset-import-template.xlsx +REACT_SAMPLE_FORMAT_EXTERNAL_RESULT_IMPORT=/External-Results-Template.csv + +# Feature flags +REACT_ENABLE_HCX=true +REACT_ENABLE_ABDM=true +REACT_ENABLE_SCRIBE=true +REACT_WARTIME_SHIFTING=true + +# JWT token refresh interval (in milliseconds) (default: 5 minutes) +REACT_JWT_TOKEN_REFRESH_INTERVAL= + +# Minimum encounter date (default: 2020-01-01) +REACT_MIN_ENCOUNTER_DATE= diff --git a/.github/workflows/cypress.yaml b/.github/workflows/cypress.yaml index f5039548e76..14a15d30ee8 100644 --- a/.github/workflows/cypress.yaml +++ b/.github/workflows/cypress.yaml @@ -18,6 +18,8 @@ jobs: fail-fast: false matrix: containers: [1, 2, 3, 4, 5, 6, 7, 8] + env: + REACT_CARE_API_URL: http://localhost:9000 steps: - name: Checkout 📥 uses: actions/checkout@v3 @@ -42,6 +44,7 @@ jobs: run: | cd care echo DISABLE_RATELIMIT=True >> docker/.prebuilt.env + echo "CORS_ALLOWED_ORIGINS=\"[\\\"http://localhost:4000\\\"]\"" >> docker/.prebuilt.env make docker_config_file=docker-compose.pre-built.yaml up make docker_config_file=docker-compose.pre-built.yaml load-dummy-data cd .. @@ -66,7 +69,7 @@ jobs: - name: Install dependencies 📦 run: npm install - - name: Build & Compile rescript files ⚙️ + - name: Build ⚙️ run: npm run build - name: Install Specific Chrome Version @@ -89,7 +92,6 @@ jobs: parallel: true group: "UI-Chrome" env: - CARE_API: http://localhost:9000 CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max_old_space_size=4096 @@ -111,7 +113,6 @@ jobs: group: "UI-Chrome" env: CYPRESS_SPLIT_TESTS: "true" - CARE_API: http://localhost:9000 CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max_old_space_size=4096 diff --git a/README.md b/README.md index 740a0857ed0..fd5328150f2 100644 --- a/README.md +++ b/README.md @@ -81,11 +81,11 @@ To ensure the quality of our pull requests, we use a variety of tools: To run cypress tests locally, you'll need to setup the backend to run locally and load dummy data required for cypress to the database. See [docs](https://github.com/ohcnetwork/care#self-hosting). -Once backend is running locally, you'll have to ensure your local front-end is connected to local backend, by setting the `CARE_API` env. +Once backend is running locally, you'll have to ensure your local front-end is connected to local backend, by setting the `REACT_CARE_API_URL` env. ```env #.env -CARE_API=http://127.0.0.1:9000 +REACT_CARE_API_URL=http://127.0.0.1:9000 ``` Once done, start the development server by running diff --git a/care.config.ts b/care.config.ts new file mode 100644 index 00000000000..87604b50030 --- /dev/null +++ b/care.config.ts @@ -0,0 +1,103 @@ +const env = import.meta.env; + +interface ILogo { + light: string; + dark: string; +} + +const logo = (value?: string, fallback?: ILogo) => { + if (!value) { + return fallback; + } + + try { + return JSON.parse(value) as ILogo; + } catch { + // TODO: define vite plugin to validate care.config.ts during build step + return fallback; + } +}; +const careConfig = { + apiUrl: env.REACT_CARE_API_URL, + + urls: { + dashboard: env.REACT_DASHBOARD_URL, + github: env.REACT_GITHUB_URL || "https://github.com/ohcnetwork", + ohcn: env.REACT_OHCN_URL || "https://ohc.network?ref=care", + }, + + headerLogo: logo(env.REACT_HEADER_LOGO, { + light: "https://cdn.ohc.network/header_logo.png", + dark: "https://cdn.ohc.network/header_logo.png", + }), + mainLogo: logo(env.REACT_MAIN_LOGO, { + light: "https://cdn.ohc.network/light-logo.svg", + dark: "https://cdn.ohc.network/black-logo.svg", + }), + stateLogo: logo(env.REACT_STATE_LOGO), + customLogo: logo(env.REACT_CUSTOM_LOGO), + customLogoAlt: logo(env.REACT_CUSTOM_LOGO_ALT), + customDescription: env.REACT_CUSTOM_DESCRIPTION, + + gmapsApiKey: + env.REACT_GMAPS_API_KEY || "AIzaSyDsBAc3y7deI5ZO3NtK5GuzKwtUzQNJNUk", + + govDataApiKey: + env.REACT_GOV_DATA_API_KEY || + "579b464db66ec23bdd000001cdd3946e44ce4aad7209ff7b23ac571b", + reCaptchaSiteKey: + env.REACT_RECAPTCHA_SITE_KEY || "6LdvxuQUAAAAADDWVflgBqyHGfq-xmvNJaToM0pN", + + kasp: { + enabled: env.REACT_KASP_ENABLED === "true", + string: env.REACT_KASP_STRING || "KASP", + fullString: + env.REACT_KASP_FULL_STRING || "Karunya Arogya Suraksha Padhathi", + }, + + sampleFormats: { + assetImport: + env.REACT_SAMPLE_FORMAT_ASSET_IMPORT || "/asset-import-template.xlsx", + externalResultImport: + env.REACT_SAMPLE_FORMAT_EXTERNAL_RESULT_IMPORT || + "/External-Results-Template.csv", + }, + + wartimeShifting: env.REACT_WARTIME_SHIFTING === "true", + + auth: { + tokenRefreshInterval: env.REACT_JWT_TOKEN_REFRESH_INTERVAL + ? parseInt(env.REACT_JWT_TOKEN_REFRESH_INTERVAL) + : 5 * 60e3, + }, + + minEncounterDate: new Date(env.REACT_MIN_ENCOUNTER_DATE || "2020-01-01"), + + // Plugins related configs... + + plausible: { + server: env.REACT_PLAUSIBLE_SERVER_URL || "https://plausible.10bedicu.in", + domain: env.REACT_PLAUSIBLE_SITE_DOMAIN || "care.ohc.network", + }, + + sentry: { + dsn: + env.REACT_SENTRY_DSN || + "https://8801155bd0b848a09de9ebf6f387ebc8@sentry.io/5183632", + environment: env.REACT_SENTRY_ENVIRONMENT || "staging", + }, + + hcx: { + enabled: env.REACT_ENABLE_HCX === "true", + }, + + abdm: { + enabled: (env.REACT_ENABLE_ABDM ?? "true") === "true", + }, + + scribe: { + enabled: env.REACT_ENABLE_SCRIBE === "true", + }, +} as const; + +export default careConfig; diff --git a/cypress.config.ts b/cypress.config.ts index 5a6f5183274..7a4c86157da 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -32,4 +32,7 @@ export default defineConfig({ requestTimeout: 15000, excludeSpecPattern: "**/*roles.cy.ts", }, + env: { + API_URL: process.env.REACT_CARE_API_URL, + }, }); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 8c1b60f3d88..776e3409511 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -2,6 +2,8 @@ import "cypress-localstorage-commands"; import { Cypress, cy } from "local-cypress"; +const apiUrl = Cypress.env("API_URL"); + Cypress.Commands.add("login", (username: string, password: string) => { cy.log(`Logging in the user: ${username}:${password}`); cy.visit("/"); @@ -14,7 +16,7 @@ Cypress.Commands.add("login", (username: string, password: string) => { Cypress.Commands.add("refreshApiLogin", (username, password) => { cy.request({ method: "POST", - url: "/api/v1/auth/login/", + url: `${apiUrl}/api/v1/auth/login/`, body: { username, password, @@ -43,7 +45,7 @@ Cypress.Commands.add("loginByApi", (username, password) => { if (tkn && token.access && token.username === username) { cy.request({ method: "POST", - url: "/api/v1/auth/token/verify/", + url: `${apiUrl}/api/v1/auth/token/verify/`, body: { token: token.access, }, @@ -69,7 +71,7 @@ Cypress.Commands.add("loginByApi", (username, password) => { Cypress.Commands.add( "awaitUrl", (url: string, disableLoginVerification = false) => { - cy.intercept(/currentuser/).as("currentuser"); + cy.intercept(/getcurrentuser/).as("currentuser"); cy.visit(url); disableLoginVerification ? cy.wait("@currentuser") diff --git a/index.html b/index.html index 8fea63f2bad..6d2c83a0a99 100644 --- a/index.html +++ b/index.html @@ -11,7 +11,7 @@ - + diff --git a/netlify.toml b/netlify.toml index a79fe05eefe..c1d30732a53 100644 --- a/netlify.toml +++ b/netlify.toml @@ -7,12 +7,6 @@ NODE_VERSION = "20.12.0" NPM_FLAGS = "--legacy-peer-deps" NODE_OPTIONS = "--max_old_space_size=4096" -[[redirects]] -from = "/api/*" -to = "https://careapi.ohc.network/api/:splat" -status = 200 -force = true - [[redirects]] from = "/*" to = "/index.html" diff --git a/src/App.tsx b/src/App.tsx index f89dfd11e74..2e7f185f80b 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,28 +1,23 @@ import { Suspense } from "react"; import Routers from "./Routers"; -import { - AppConfigProvider, - AuthUserProvider, - HistoryAPIProvider, -} from "./Providers"; import ThemedFavicon from "./CAREUI/misc/ThemedFavicon"; import Intergrations from "./Integrations"; import Loading from "./Components/Common/Loading"; +import HistoryAPIProvider from "./Providers/HistoryAPIProvider"; +import AuthUserProvider from "./Providers/AuthUserProvider"; const App = () => { return ( }> - - }> - - + }> + + - {/* Integrations */} - - - + {/* Integrations */} + + ); diff --git a/src/Common/constants.tsx b/src/Common/constants.tsx index 5fa8748b399..681fe874475 100644 --- a/src/Common/constants.tsx +++ b/src/Common/constants.tsx @@ -1,4 +1,3 @@ -import { IConfig } from "./hooks/useConfig"; import { PatientCategory } from "../Components/Facility/models"; import { SortOption } from "../Components/Common/SortDropdown"; import { dateQueryString } from "../Utils/utils"; @@ -9,6 +8,7 @@ import { ConsentHIType, ConsentPurpose, } from "../Components/ABDM/types/consent"; +import careConfig from "@careConfig"; export const RESULTS_PER_PAGE_LIMIT = 14; export const PAGINATION_LIMIT = 36; @@ -217,33 +217,30 @@ export const DISCHARGED_PATIENT_SORT_OPTIONS: SortOption[] = [ { isAscending: false, value: "-name" }, ]; -export const getBedTypes = ({ - kasp_enabled, - kasp_string, -}: Pick) => { - const kaspBedTypes = kasp_enabled - ? [ - { id: 40, text: kasp_string + " Ordinary Beds" }, - { id: 60, text: kasp_string + " Oxygen beds" }, - { id: 50, text: kasp_string + " ICU (ICU without ventilator)" }, - { id: 70, text: kasp_string + " ICU (ICU with ventilator)" }, - ] - : []; - - return [ - { id: 1, text: "Ordinary Beds" }, - { id: 150, text: "Oxygen beds" }, - { id: 10, text: "ICU (ICU without ventilator)" }, - { id: 20, text: "Ventilator (ICU with ventilator)" }, - { id: 30, text: "Covid Ordinary Beds" }, - { id: 120, text: "Covid Oxygen beds" }, - { id: 110, text: "Covid ICU (ICU without ventilator)" }, - { id: 100, text: "Covid Ventilators (ICU with ventilator)" }, - ...kaspBedTypes, - { id: 2, text: "Hostel" }, - { id: 3, text: "Single Room with Attached Bathroom" }, - ]; -}; +const { kasp } = careConfig; + +const KASP_BED_TYPES = kasp.enabled + ? [ + { id: 40, text: kasp.string + " Ordinary Beds" }, + { id: 60, text: kasp.string + " Oxygen beds" }, + { id: 50, text: kasp.string + " ICU (ICU without ventilator)" }, + { id: 70, text: kasp.string + " ICU (ICU with ventilator)" }, + ] + : []; + +export const BED_TYPES: OptionsType[] = [ + { id: 1, text: "Ordinary Beds" }, + { id: 150, text: "Oxygen beds" }, + { id: 10, text: "ICU (ICU without ventilator)" }, + { id: 20, text: "Ventilator (ICU with ventilator)" }, + { id: 30, text: "Covid Ordinary Beds" }, + { id: 120, text: "Covid Oxygen beds" }, + { id: 110, text: "Covid ICU (ICU without ventilator)" }, + { id: 100, text: "Covid Ventilators (ICU with ventilator)" }, + ...KASP_BED_TYPES, + { id: 2, text: "Hostel" }, + { id: 3, text: "Single Room with Attached Bathroom" }, +]; export const DOCTOR_SPECIALIZATION: Array = [ { id: 1, text: "General Medicine" }, diff --git a/src/Common/hooks/useConfig.ts b/src/Common/hooks/useConfig.ts deleted file mode 100644 index 38e2336d583..00000000000 --- a/src/Common/hooks/useConfig.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { createContext, useContext } from "react"; - -export const AppConfigContext = createContext(null); - -interface ILogo { - light: string; - dark: string; -} - -export interface IConfig { - dashboard_url?: string; - github_url: string; - ohcn_url: string; - site_url: string; - analytics_server_url: string; - - /** - * The main logo of the app displayed on login and sidebar header. - */ - main_logo: ILogo; - /** - * If present, the image will be displayed on the login page before the main logo. - */ - state_logo?: ILogo; - /** - * if present, this replaces the state logo on the login page only. - */ - custom_logo?: ILogo; - /** - * if present, this replaces the main logo on the login page only. - */ - custom_logo_alt?: ILogo; - - custom_description?: string; - - /** - * The API key for the Google Maps API used for location picker. - */ - gmaps_api_key: string; - /** - * The API key for the data.gov.in API used for pincode auto-complete. - */ - gov_data_api_key: string; - recaptcha_site_key: string; - /** - * SENTRY_DSN - */ - sentry_dsn: string; - /** - * SENTRY_ENVIRONMENT - */ - sentry_environment: string; - - /** - * The header banner is displayed on the top of - * the shift print form if the facility is kasp. - */ - header_logo: ILogo; - kasp_enabled: boolean; - kasp_string: string; - kasp_full_string: string; - /** - * URL of the sample format for asset import. - */ - sample_format_asset_import: string; - /** - * URL of the sample format for external result import. - */ - sample_format_external_result_import: string; - /** - * Env to enable HCX features - */ - enable_hcx: boolean; - /** - * Env to enable ABDM features - */ - enable_abdm: boolean; - /** - * Env to enable scribe features - */ - enable_scribe: boolean; - /** - * Env to toggle peacetime and wartime shifting - */ - wartime_shifting: boolean; - jwt_token_refresh_interval?: number; - - /* - * Minimum date for a possible consultation encounter. - */ - min_encounter_date: string; -} - -const useConfig = () => { - const config = useContext(AppConfigContext); - - if (!config) { - throw new Error("useConfig must be used within an AppConfigProvider"); - } - - return config; -}; - -export default useConfig; diff --git a/src/Common/hooks/useFilters.tsx b/src/Common/hooks/useFilters.tsx index c0919be9acc..a4b924edcdb 100644 --- a/src/Common/hooks/useFilters.tsx +++ b/src/Common/hooks/useFilters.tsx @@ -3,9 +3,9 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import GenericFilterBadge from "../../CAREUI/display/FilterBadge"; import PaginationComponent from "../../Components/Common/Pagination"; -import useConfig from "./useConfig"; import { classNames, humanizeStrings } from "../../Utils/utils"; import FiltersCache from "../../Utils/FiltersCache"; +import careConfig from "@careConfig"; export type FilterState = Record; @@ -27,7 +27,6 @@ export default function useFilters({ cacheBlacklist?: string[]; }) { const { t } = useTranslation(); - const { kasp_string } = useConfig(); const hasPagination = limit > 0; const [showFilters, setShowFilters] = useState(false); const [qParams, _setQueryParams] = useQueryParams(); @@ -155,8 +154,9 @@ export default function useFilters({ return { name, value, paramKey }; }, kasp(nameSuffix = "", paramKey = "is_kasp") { - const name = nameSuffix ? kasp_string + " " + nameSuffix : kasp_string; - const [trueLabel, falseLabel] = [kasp_string, "Non " + kasp_string]; + const { kasp } = careConfig; + const name = nameSuffix ? kasp.string + " " + nameSuffix : kasp.string; + const [trueLabel, falseLabel] = [kasp.string, "Non " + kasp.string]; return badgeUtils.boolean(name, paramKey, { trueLabel, falseLabel }); }, }; diff --git a/src/Components/Assets/AssetImportModal.tsx b/src/Components/Assets/AssetImportModal.tsx index d553e30b79e..6ff51e293af 100644 --- a/src/Components/Assets/AssetImportModal.tsx +++ b/src/Components/Assets/AssetImportModal.tsx @@ -5,12 +5,14 @@ import { AssetData } from "./AssetTypes"; import * as Notification from "../../Utils/Notifications.js"; import { Cancel } from "../Common/components/ButtonV2"; import { Link } from "raviger"; -import { LocalStorageKeys, AssetImportSchema } from "../../Common/constants"; -import useConfig from "../../Common/hooks/useConfig"; +import { AssetImportSchema } from "../../Common/constants"; import DialogModal from "../Common/Dialog"; import useQuery from "../../Utils/request/useQuery"; import routes from "../../Redux/api"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; +import careConfig from "@careConfig"; +import request from "../../Utils/request/request"; + const ExcelFileDragAndDrop = lazy( () => import("../Common/ExcelFIleDragAndDrop"), ); @@ -29,7 +31,6 @@ const AssetImportModal = ({ open, onClose, facility, onUpdate }: Props) => { const [errors, setErrors] = useState({ location: "", }); - const { sample_format_asset_import } = useConfig(); const closeModal = () => { onClose && onClose(); @@ -85,17 +86,8 @@ const AssetImportModal = ({ open, onClose, facility, onUpdate }: Props) => { asset_data["warranty_amc_end_of_validity"] = asset.warranty_amc_end_of_validity; - const response = await fetch("/api/v1/asset/", { - method: "POST", - headers: { - "Content-Type": "application/json", - Authorization: - "Bearer " + localStorage.getItem(LocalStorageKeys.accessToken), - }, - body: JSON.stringify(asset_data), - }); - const data = await response.json(); - if (response.status !== 201) { + const { res } = await request(routes.createAsset, { body: asset_data }); + if (!res?.ok) { Notification.Error({ msg: "Error importing asset: " + asset.name + " " + JSON.stringify(data), @@ -176,7 +168,7 @@ const AssetImportModal = ({ open, onClose, facility, onUpdate }: Props) => { handleSubmit={handleUpload} loading={isImporting} schema={AssetImportSchema} - sampleLink={sample_format_asset_import} + sampleLink={careConfig.sampleFormats.assetImport} setIsValid={setIsValid} /> diff --git a/src/Components/Auth/Login.tsx b/src/Components/Auth/Login.tsx index d0d055413cb..17ecf061ef2 100644 --- a/src/Components/Auth/Login.tsx +++ b/src/Components/Auth/Login.tsx @@ -7,26 +7,25 @@ import * as Notification from "../../Utils/Notifications.js"; import LegendInput from "../../CAREUI/interactive/LegendInput"; import LanguageSelectorLogin from "../Common/LanguageSelectorLogin"; import CareIcon from "../../CAREUI/icons/CareIcon"; -import useConfig from "../../Common/hooks/useConfig"; import CircularProgress from "../Common/components/CircularProgress"; import ReactMarkdown from "react-markdown"; import rehypeRaw from "rehype-raw"; import { useAuthContext } from "../../Common/hooks/useAuthUser"; import FiltersCache from "../../Utils/FiltersCache"; import { classNames } from "../../Utils/utils"; +import careConfig from "@careConfig"; export const Login = (props: { forgot?: boolean }) => { const { signIn } = useAuthContext(); const { - main_logo, - recaptcha_site_key, - github_url, - ohcn_url, - state_logo, - custom_logo, - custom_logo_alt, - custom_description, - } = useConfig(); + mainLogo, + reCaptchaSiteKey, + urls, + stateLogo, + customLogo, + customLogoAlt, + customDescription, + } = careConfig; const initForm: any = { username: "", password: "", @@ -162,10 +161,10 @@ export const Login = (props: { forgot?: boolean }) => {
- {(custom_logo || state_logo) && ( + {(customLogo || stateLogo) && ( <> state logo @@ -173,13 +172,13 @@ export const Login = (props: { forgot?: boolean }) => { )} Open Healthcare Network logo @@ -189,13 +188,13 @@ export const Login = (props: { forgot?: boolean }) => {

{t("care")}

- {custom_description ? ( + {customDescription ? (
- {custom_description || t("goal")} + {customDescription || t("goal")}
) : ( @@ -220,7 +219,7 @@ export const Login = (props: { forgot?: boolean }) => { />
{
{
- {(custom_logo || state_logo) && ( + {(customLogo || stateLogo) && ( <> state logo @@ -265,7 +264,7 @@ export const Login = (props: { forgot?: boolean }) => { )} care logo @@ -310,7 +309,7 @@ export const Login = (props: { forgot?: boolean }) => { {isCaptchaEnabled && (
{errors.captcha} diff --git a/src/Components/Common/GLocationPicker.tsx b/src/Components/Common/GLocationPicker.tsx index 5119d219e73..0b03d2775ce 100644 --- a/src/Components/Common/GLocationPicker.tsx +++ b/src/Components/Common/GLocationPicker.tsx @@ -4,8 +4,8 @@ import { deepEqual } from "../../Common/utils"; import { isLatLngLiteral } from "@googlemaps/typescript-guards"; import Spinner from "./Spinner"; import CareIcon from "../../CAREUI/icons/CareIcon"; -import useConfig from "../../Common/hooks/useConfig"; import { PopoverButton } from "@headlessui/react"; +import careConfig from "@careConfig"; interface GLocationPickerProps { lat: number; @@ -24,7 +24,6 @@ const GLocationPicker = ({ handleOnClose, handleOnSelectCurrentLocation, }: GLocationPickerProps) => { - const { gmaps_api_key } = useConfig(); const [location, setLocation] = React.useState( null, ); @@ -87,7 +86,7 @@ const GLocationPicker = ({
diff --git a/src/Components/Common/Sidebar/Sidebar.tsx b/src/Components/Common/Sidebar/Sidebar.tsx index f454c9c6ab9..52e5e0680fa 100644 --- a/src/Components/Common/Sidebar/Sidebar.tsx +++ b/src/Components/Common/Sidebar/Sidebar.tsx @@ -4,11 +4,11 @@ import SidebarUserCard from "./SidebarUserCard"; import NotificationItem from "../../Notifications/NotificationsList"; import useActiveLink from "../../../Common/hooks/useActiveLink"; import CareIcon, { IconName } from "../../../CAREUI/icons/CareIcon"; -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"; +import careConfig from "@careConfig"; export const SIDEBAR_SHRINK_PREFERENCE_KEY = "sidebarShrinkPreference"; @@ -62,10 +62,8 @@ const StatelessSidebar = ({ { text: "Notice Board", to: "/notice_board", icon: "l-meeting-board" }, ]; - const { main_logo } = useConfig(); const activeLink = useActiveLink(); const Item = shrinked ? ShrinkedSidebarItem : SidebarItem; - const { dashboard_url } = useConfig(); const indicatorRef = useRef(null); const activeLinkRef = useRef(null); @@ -75,7 +73,7 @@ const StatelessSidebar = ({ useEffect(() => { if (!indicatorRef.current) return; const index = NavItems.findIndex((item) => item.to === activeLink); - const navItemCount = NavItems.length + (dashboard_url ? 2 : 1); // +2 for notification and dashboard + const navItemCount = NavItems.length + (careConfig.urls.dashboard ? 2 : 1); // +2 for notification and dashboard if (index !== -1) { // Haha math go brrrrrrrrr @@ -118,7 +116,7 @@ const StatelessSidebar = ({ className={`${ shrinked ? "mx-auto" : "ml-5" } mb-2 h-5 self-start transition md:mb-5 md:h-8`} - src={shrinked ? LOGO_COLLAPSE : main_logo.light} + src={shrinked ? LOGO_COLLAPSE : careConfig.mainLogo?.light} />
{/* flexible spacing */} @@ -151,10 +149,10 @@ const StatelessSidebar = ({ handleOverflow={handleOverflow} onClickCB={() => onItemClick && onItemClick(false)} /> - {dashboard_url && ( + {careConfig.urls.dashboard && ( } external handleOverflow={handleOverflow} diff --git a/src/Components/ExternalResult/ExternalResultImportModal.tsx b/src/Components/ExternalResult/ExternalResultImportModal.tsx index 06ba575b3f2..79df1cfdc09 100644 --- a/src/Components/ExternalResult/ExternalResultImportModal.tsx +++ b/src/Components/ExternalResult/ExternalResultImportModal.tsx @@ -1,13 +1,13 @@ import _ from "lodash-es"; import { navigate } from "raviger"; import { useEffect, useState, lazy } from "react"; -import useConfig from "../../Common/hooks/useConfig"; import * as Notification from "../../Utils/Notifications.js"; import request from "../../Utils/request/request"; import routes from "../../Redux/api"; import { ExternalResultImportSchema } from "../../Common/constants"; import DialogModal from "../Common/Dialog"; import { IExternalResult } from "./models"; +import careConfig from "@careConfig"; const ExcelFileDragAndDrop = lazy( () => import("../Common/ExcelFIleDragAndDrop"), ); @@ -18,7 +18,6 @@ interface Props { } export default function ExternalResultImportModal({ open, onClose }: Props) { - const { sample_format_external_result_import } = useConfig(); const [loading, setLoading] = useState(false); const fetchUser = async () => { @@ -105,7 +104,7 @@ export default function ExternalResultImportModal({ open, onClose }: Props) { onClose={onClose} handleSubmit={handleSubmit} loading={loading} - sampleLink={sample_format_external_result_import} + sampleLink={careConfig.sampleFormats.externalResultImport} schema={ExternalResultImportSchema} /> diff --git a/src/Components/Facility/BedCapacity.tsx b/src/Components/Facility/BedCapacity.tsx index c0f239203d5..a4437823d5d 100644 --- a/src/Components/Facility/BedCapacity.tsx +++ b/src/Components/Facility/BedCapacity.tsx @@ -1,15 +1,14 @@ import { useEffect, useReducer, useState } from "react"; import * as Notification from "../../Utils/Notifications.js"; -import { CapacityModal, OptionsType } from "./models"; +import { CapacityModal } from "./models"; import TextFormField from "../Form/FormFields/TextFormField"; import { Cancel, Submit } from "../Common/components/ButtonV2"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; import { FieldChangeEvent } from "../Form/FormFields/Utils"; -import useConfig from "../../Common/hooks/useConfig"; -import { getBedTypes } from "../../Common/constants"; import routes from "../../Redux/api"; import request from "../../Utils/request/request"; import { useTranslation } from "react-i18next"; +import { BED_TYPES } from "../../Common/constants.js"; interface BedCapacityProps extends CapacityModal { facilityId: string; @@ -51,11 +50,10 @@ const bedCountReducer = (state = initialState, action: any) => { export const BedCapacity = (props: BedCapacityProps) => { const { t } = useTranslation(); - const config = useConfig(); const { facilityId, handleClose, handleUpdate, className, id } = props; const [state, dispatch] = useReducer(bedCountReducer, initialState); + const [bedTypes, setBedTypes] = useState(BED_TYPES); const [isLastOptionType, setIsLastOptionType] = useState(false); - const [bedTypes, setBedTypes] = useState(getBedTypes(config)); const [isLoading, setIsLoading] = useState(false); const headerText = !id ? "Add Bed Capacity" : "Edit Bed Capacity"; @@ -73,11 +71,11 @@ export const BedCapacity = (props: BedCapacityProps) => { if (capacityQuery?.data) { const existingData = capacityQuery.data?.results; // if all options are diabled - if (existingData.length === getBedTypes(config).length) { + if (existingData.length === BED_TYPES.length) { return; } // disable existing bed types - const updatedBedTypes = getBedTypes(config).map((type: OptionsType) => { + const updatedBedTypes = BED_TYPES.map((type) => { const isExisting = existingData.find( (i: CapacityModal) => i.room_type === type.id, ); @@ -113,8 +111,7 @@ export const BedCapacity = (props: BedCapacityProps) => { useEffect(() => { const lastBedType = - bedTypes.filter((i: OptionsType) => i.disabled).length === - getBedTypes(config).length - 1; + bedTypes.filter((i) => i.disabled).length === BED_TYPES.length - 1; setIsLastOptionType(lastBedType); }, [bedTypes]); @@ -179,7 +176,7 @@ export const BedCapacity = (props: BedCapacityProps) => { ); setIsLoading(false); if (data) { - const updatedBedTypes = bedTypes.map((type: OptionsType) => { + const updatedBedTypes = bedTypes.map((type) => { return { ...type, disabled: data.room_type !== type.id ? type.disabled : true, diff --git a/src/Components/Facility/ConsultationCard.tsx b/src/Components/Facility/ConsultationCard.tsx index 12f4bf0a1e3..23b9cb86567 100644 --- a/src/Components/Facility/ConsultationCard.tsx +++ b/src/Components/Facility/ConsultationCard.tsx @@ -4,12 +4,12 @@ import { formatDateTime } from "../../Utils/utils"; import ButtonV2 from "../Common/components/ButtonV2"; import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; import RelativeDateUserMention from "../Common/RelativeDateUserMention"; -import useConfig from "../../Common/hooks/useConfig"; import Chip from "../../CAREUI/display/Chip"; import * as Notification from "../../Utils/Notifications.js"; import { useState } from "react"; import DialogModal from "../Common/Dialog.js"; import Beds from "./Consultations/Beds"; +import careConfig from "@careConfig"; interface ConsultationProps { itemData: ConsultationModel; @@ -19,7 +19,6 @@ interface ConsultationProps { export const ConsultationCard = (props: ConsultationProps) => { const { itemData, isLastConsultation, refetch } = props; - const { kasp_string } = useConfig(); const [open, setOpen] = useState(false); const bedDialogTitle = itemData.discharge_date ? "Bed History" @@ -51,7 +50,7 @@ export const ConsultationCard = (props: ConsultationProps) => {
{itemData.is_kasp && (
- {kasp_string} + {careConfig.kasp.string}
)} @@ -85,7 +84,7 @@ export const ConsultationCard = (props: ConsultationProps) => {
- {kasp_string} Enabled date{" "} + {careConfig.kasp.string} Enabled date{" "}
{itemData.kasp_enabled_date diff --git a/src/Components/Facility/ConsultationForm.tsx b/src/Components/Facility/ConsultationForm.tsx index 1d498ea3d14..f9faffaba0f 100644 --- a/src/Components/Facility/ConsultationForm.tsx +++ b/src/Components/Facility/ConsultationForm.tsx @@ -38,7 +38,6 @@ import { UserBareMinimum } from "../Users/models"; import { navigate } from "raviger"; import useAppHistory from "../../Common/hooks/useAppHistory"; -import useConfig from "../../Common/hooks/useConfig"; import useVisibility from "../../Utils/useVisibility"; import dayjs from "../../Utils/dayjs"; import RouteToFacilitySelect, { @@ -65,6 +64,7 @@ import { EncounterSymptomsBuilder, CreateSymptomsBuilder, } from "../Symptoms/SymptomsBuilder.js"; +import careConfig from "@careConfig"; const Loading = lazy(() => import("../Common/Loading")); const PageTitle = lazy(() => import("../Common/PageTitle")); @@ -229,7 +229,6 @@ type Props = { export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { const { goBack } = useAppHistory(); - const { kasp_enabled, kasp_string } = useConfig(); const submitController = useRef(); const [state, dispatch] = useAutoSaveReducer( consultationFormReducer, @@ -255,8 +254,6 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { const [disabledFields, setDisabledFields] = useState([]); - const { min_encounter_date } = useConfig(); - const sections = { "Consultation Details": { iconClass: "l-medkit", @@ -474,11 +471,12 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { invalidForm = true; } if ( - min_encounter_date && - dayjs(state.form.encounter_date).isBefore(dayjs(min_encounter_date)) + dayjs(state.form.encounter_date).isBefore( + careConfig.minEncounterDate, + ) ) { errors[field] = - `Admission date cannot be before ${min_encounter_date}`; + `Admission date cannot be before ${careConfig.minEncounterDate}`; invalidForm = true; } return; @@ -552,7 +550,7 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { case "is_kasp": if (!state.form[field]) { errors[field] = - `Please select an option, ${kasp_string} is mandatory`; + `Please select an option, ${careConfig.kasp.string} is mandatory`; invalidForm = true; } return; @@ -1226,11 +1224,9 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { "YYYY-MM-DDTHH:mm", )} max={dayjs().format("YYYY-MM-DDTHH:mm")} - min={ - min_encounter_date - ? dayjs(min_encounter_date).format("YYYY-MM-DDTHH:mm") - : undefined - } + min={dayjs(careConfig.minEncounterDate).format( + "YYYY-MM-DDTHH:mm", + )} /> {dayjs().diff(state.form.encounter_date, "day") > 30 && (
@@ -1402,12 +1398,12 @@ export const ConsultationForm = ({ facilityId, patientId, id }: Props) => { />
- {kasp_enabled && ( + {careConfig.kasp.enabled && ( )} diff --git a/src/Components/Facility/CoverImageEditModal.tsx b/src/Components/Facility/CoverImageEditModal.tsx index d521d55c7ce..bade873c5fa 100644 --- a/src/Components/Facility/CoverImageEditModal.tsx +++ b/src/Components/Facility/CoverImageEditModal.tsx @@ -20,6 +20,8 @@ import DialogModal from "../Common/Dialog"; import request from "../../Utils/request/request"; import routes from "../../Redux/api"; import uploadFile from "../../Utils/request/uploadFile"; +import careConfig from "@careConfig"; + interface Props { open: boolean; onClose: (() => void) | undefined; @@ -116,7 +118,7 @@ const CoverImageEditModal = ({ const formData = new FormData(); formData.append("cover_image", selectedFile); - const url = `/api/v1/facility/${facility.id}/cover_image/`; + const url = `${careConfig.apiUrl}/api/v1/facility/${facility.id}/cover_image/`; setIsProcessing(true); uploadFile( diff --git a/src/Components/Facility/DischargeModal.tsx b/src/Components/Facility/DischargeModal.tsx index fe99b4ddd53..ac86ff02c55 100644 --- a/src/Components/Facility/DischargeModal.tsx +++ b/src/Components/Facility/DischargeModal.tsx @@ -16,7 +16,6 @@ import { SelectFormField } from "../Form/FormFields/SelectFormField"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; import { dischargePatient } from "../../Redux/actions"; -import useConfig from "../../Common/hooks/useConfig"; import { useDispatch } from "react-redux"; import { useMessageListener } from "../../Common/hooks/useMessageListener"; import PrescriptionBuilder from "../Medicine/PrescriptionBuilder"; @@ -28,6 +27,7 @@ import { FieldError } from "../Form/FieldValidators"; import { useTranslation } from "react-i18next"; import useConfirmedAction from "../../Common/hooks/useConfirmedAction"; import ConfirmDialog from "../Common/ConfirmDialog"; +import careConfig from "@careConfig"; interface PreDischargeFormInterface { new_discharge_reason: number | null; @@ -61,7 +61,6 @@ const DischargeModal = ({ death_datetime = dayjs().format("YYYY-MM-DDTHH:mm"), }: IProps) => { const { t } = useTranslation(); - const { enable_hcx } = useConfig(); const dispatch: any = useDispatch(); const [preDischargeForm, setPreDischargeForm] = @@ -385,7 +384,7 @@ const DischargeModal = ({ )}
- {enable_hcx && ( + {careConfig.hcx.enabled && ( // TODO: if policy and approved pre-auth exists

Claim Insurance

diff --git a/src/Components/Facility/FacilityBedCapacity.tsx b/src/Components/Facility/FacilityBedCapacity.tsx index e2a24b3fab3..77481fa8716 100644 --- a/src/Components/Facility/FacilityBedCapacity.tsx +++ b/src/Components/Facility/FacilityBedCapacity.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; -import { getBedTypes } from "../../Common/constants"; import routes from "../../Redux/api"; import { NonReadOnlyUsers } from "../../Utils/AuthorizeFor"; import useQuery from "../../Utils/request/useQuery"; @@ -7,12 +6,11 @@ import DialogModal from "../Common/Dialog"; import ButtonV2 from "../Common/components/ButtonV2"; import { BedCapacity } from "./BedCapacity"; import BedTypeCard from "./BedTypeCard"; -import useConfig from "../../Common/hooks/useConfig"; import CareIcon from "../../CAREUI/icons/CareIcon"; +import { BED_TYPES } from "../../Common/constants"; export const FacilityBedCapacity = (props: any) => { const [bedCapacityModalOpen, setBedCapacityModalOpen] = useState(false); - const config = useConfig(); const capacityQuery = useQuery(routes.getCapacity, { pathParams: { facilityId: props.facilityId }, @@ -45,7 +43,7 @@ export const FacilityBedCapacity = (props: any) => { return; }} /> - {getBedTypes(config).map((x) => { + {BED_TYPES.map((x) => { const res = capacityQuery.data?.results.find((data) => { return data.room_type === x.id; }); diff --git a/src/Components/Facility/FacilityCard.tsx b/src/Components/Facility/FacilityCard.tsx index 858386b72e6..c7fbf728dc9 100644 --- a/src/Components/Facility/FacilityCard.tsx +++ b/src/Components/Facility/FacilityCard.tsx @@ -9,14 +9,13 @@ import CareIcon from "../../CAREUI/icons/CareIcon"; import { formatPhoneNumber, parsePhoneNumber } from "../../Utils/utils"; import DialogModal from "../Common/Dialog"; import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; -import useConfig from "../../Common/hooks/useConfig"; import { classNames } from "../../Utils/utils"; import request from "../../Utils/request/request"; import routes from "../../Redux/api"; +import careConfig from "@careConfig"; export const FacilityCard = (props: { facility: any; userType: any }) => { const { facility, userType } = props; - const { kasp_string } = useConfig(); const { t } = useTranslation(); const [notifyModalFor, setNotifyModalFor] = useState(undefined); @@ -92,7 +91,7 @@ export const FacilityCard = (props: { facility: any; userType: any }) => {
{facility.kasp_empanelled && (
- {kasp_string} + {careConfig.kasp.string}
)}
import("../Common/Loading")); @@ -143,7 +143,6 @@ const facilityCreateReducer = (state = initialState, action: FormAction) => { export const FacilityCreate = (props: FacilityProps) => { const { t } = useTranslation(); - const { gov_data_api_key, kasp_string, kasp_enabled } = useConfig(); const { facilityId } = props; const [state, dispatch] = useAutoSaveReducer( @@ -327,7 +326,10 @@ export const FacilityCreate = (props: FacilityProps) => { if (!validatePincode(e.value)) return; - const pincodeDetails = await getPincodeDetails(e.value, gov_data_api_key); + const pincodeDetails = await getPincodeDetails( + e.value, + careConfig.govDataApiKey, + ); if (!pincodeDetails) return; const matchedState = (stateData ? stateData.results : []).find((state) => { @@ -561,7 +563,7 @@ export const FacilityCreate = (props: FacilityProps) => { return; }} /> - {getBedTypes({ kasp_string, kasp_enabled }).map((x) => { + {BED_TYPES.map((x) => { const res = capacityData.find((data) => { return data.room_type === x.id; }); @@ -922,10 +924,10 @@ export const FacilityCreate = (props: FacilityProps) => { />
- {kasp_enabled && ( + {careConfig.kasp.enabled && ( (o ? "Yes" : "No")} optionValue={(o) => String(o)} diff --git a/src/Components/HCX/InsuranceDetailsBuilder.tsx b/src/Components/HCX/InsuranceDetailsBuilder.tsx index 1e401410ff0..2c51d3b90d3 100644 --- a/src/Components/HCX/InsuranceDetailsBuilder.tsx +++ b/src/Components/HCX/InsuranceDetailsBuilder.tsx @@ -12,7 +12,7 @@ import { useDispatch } from "react-redux"; import { HCXActions } from "../../Redux/actions"; import { classNames } from "../../Utils/utils"; import InsurerAutocomplete from "./InsurerAutocomplete"; -import useConfig from "../../Common/hooks/useConfig"; +import careConfig from "@careConfig"; type Props = FormFieldBaseProps & { gridView?: boolean }; @@ -93,7 +93,6 @@ const InsuranceDetailEditCard = ({ handleRemove: () => void; gridView?: boolean; }) => { - const { enable_hcx } = useConfig(); const seletedInsurer = policy.insurer_id && policy.insurer_name ? { code: policy.insurer_id, name: policy.insurer_name } @@ -133,7 +132,7 @@ const InsuranceDetailEditCard = ({ value={policy.policy_id} onChange={handleUpdate} /> - {enable_hcx ? ( + {careConfig.hcx.enabled ? (

{encounter?.facility_name}

- care logo + care logo
diff --git a/src/Components/Patient/PatientFilter.tsx b/src/Components/Patient/PatientFilter.tsx index b69436171d8..31c54c5b9c4 100644 --- a/src/Components/Patient/PatientFilter.tsx +++ b/src/Components/Patient/PatientFilter.tsx @@ -10,7 +10,6 @@ import { PATIENT_FILTER_CATEGORIES, RATION_CARD_CATEGORY, } from "../../Common/constants"; -import useConfig from "../../Common/hooks/useConfig"; import useMergeState from "../../Common/hooks/useMergeState"; import { dateQueryString } from "../../Utils/utils"; import { DateRange } from "../Common/DateRangeInputV2"; @@ -35,6 +34,7 @@ import request from "../../Utils/request/request"; import useAuthUser from "../../Common/hooks/useAuthUser"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; import { useTranslation } from "react-i18next"; +import careConfig from "@careConfig"; const getDate = (value: any) => value && dayjs(value).isValid() && dayjs(value).toDate(); @@ -42,7 +42,6 @@ const getDate = (value: any) => export default function PatientFilter(props: any) { const { t } = useTranslation(); const authUser = useAuthUser(); - const { kasp_enabled, kasp_string } = useConfig(); const { filter, onChange, closeFilter, removeFilters } = props; const [filterState, setFilterState] = useMergeState({ @@ -702,14 +701,18 @@ export default function PatientFilter(props: any) { className="w-full rounded-md" >
- {kasp_enabled && ( + {careConfig.kasp.enabled && (
- {kasp_string} + + {careConfig.kasp.string} + - o ? `Show ${kasp_string}` : `Show Non ${kasp_string}` + o + ? `Show ${careConfig.kasp.string}` + : `Show Non ${careConfig.kasp.string}` } value={filterState.is_kasp} onChange={(v) => setFilterState({ ...filterState, is_kasp: v })} diff --git a/src/Components/Patient/PatientInfoCard.tsx b/src/Components/Patient/PatientInfoCard.tsx index 005c93521d6..f301db6333e 100644 --- a/src/Components/Patient/PatientInfoCard.tsx +++ b/src/Components/Patient/PatientInfoCard.tsx @@ -12,7 +12,6 @@ import { Switch, MenuItem, Field, Label } from "@headlessui/react"; import { Link, navigate } from "raviger"; import { useState } from "react"; import CareIcon from "../../CAREUI/icons/CareIcon.js"; -import useConfig from "../../Common/hooks/useConfig.js"; import dayjs from "../../Utils/dayjs.js"; import { classNames, @@ -43,6 +42,7 @@ import useQuery from "../../Utils/request/useQuery.js"; import FetchRecordsModal from "../ABDM/FetchRecordsModal.js"; import { SkillModel } from "../Users/models.js"; import { AuthorizedForConsultationRelatedActions } from "../../CAREUI/misc/AuthorizedChild.js"; +import careConfig from "@careConfig"; const formatSkills = (arr: SkillModel[]) => { const skills = arr.map((skill) => skill.skill_object.name); @@ -73,8 +73,6 @@ export default function PatientInfoCard(props: { const [openDischargeSummaryDialog, setOpenDischargeSummaryDialog] = useState(false); const [openDischargeDialog, setOpenDischargeDialog] = useState(false); - - const { enable_hcx, enable_abdm } = useConfig(); const [showLinkCareContext, setShowLinkCareContext] = useState(false); const patient = props.patient; @@ -665,7 +663,7 @@ export default function PatientInfoCard(props: { ], ] .concat( - enable_hcx + careConfig.hcx.enabled ? [ [ `/facility/${patient.facility}/patient/${patient.id}/consultation/${consultation?.id}/claims`, @@ -735,7 +733,7 @@ export default function PatientInfoCard(props: {
- {enable_abdm && + {careConfig.abdm.enabled && (patient.abha_number ? ( <> diff --git a/src/Components/Patient/PatientRegister.tsx b/src/Components/Patient/PatientRegister.tsx index 3b38c3d54af..c25a6365e70 100644 --- a/src/Components/Patient/PatientRegister.tsx +++ b/src/Components/Patient/PatientRegister.tsx @@ -61,7 +61,6 @@ import countryList from "../../Common/static/countries.json"; import { debounce } from "lodash-es"; import useAppHistory from "../../Common/hooks/useAppHistory"; -import useConfig from "../../Common/hooks/useConfig"; import { validatePincode } from "../../Common/validation"; import { FormContextValue } from "../Form/FormContext.js"; import useAuthUser from "../../Common/hooks/useAuthUser.js"; @@ -73,6 +72,7 @@ import SelectMenuV2 from "../Form/SelectMenuV2.js"; import _ from "lodash"; import { ILocalBodies } from "../ExternalResult/models.js"; import { useTranslation } from "react-i18next"; +import careConfig from "@careConfig"; const Loading = lazy(() => import("../Common/Loading")); const PageTitle = lazy(() => import("../Common/PageTitle")); @@ -181,7 +181,6 @@ export const PatientRegister = (props: PatientRegisterProps) => { const authUser = useAuthUser(); const { t } = useTranslation(); const { goBack } = useAppHistory(); - const { gov_data_api_key, enable_hcx, enable_abdm } = useConfig(); const { facilityId, id } = props; const [state, dispatch] = useReducer(patientFormReducer, initialState); const [showAlertMessage, setAlertMessage] = useState({ @@ -504,7 +503,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { const errors: Partial> = {}; const insuranceDetailsError = insuranceDetails - .map((policy) => HCXPolicyValidator(policy, enable_hcx)) + .map((policy) => HCXPolicyValidator(policy, careConfig.hcx.enabled)) .find((error) => !!error); setInsuranceDetailsError(insuranceDetailsError); @@ -644,7 +643,10 @@ export const PatientRegister = (props: PatientRegisterProps) => { const handlePincodeChange = async (e: any, setField: any) => { if (!validatePincode(e.value)) return; - const pincodeDetails = await getPincodeDetails(e.value, gov_data_api_key); + const pincodeDetails = await getPincodeDetails( + e.value, + careConfig.govDataApiKey, + ); if (!pincodeDetails) return; const matchedState = stateData?.results?.find((state) => { @@ -792,7 +794,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { body: policy, }); - if (enable_hcx && policyData?.id) { + if (careConfig.hcx.enabled && policyData?.id) { await request(routes.hcxCheckEligibility, { body: { policy: policyData?.id }, onResponse: ({ res }) => { @@ -1181,7 +1183,7 @@ export const PatientRegister = (props: PatientRegisterProps) => { Import From External Results
- {enable_abdm && ( + {careConfig.abdm.enabled && (

ABHA Details diff --git a/src/Components/Patient/ShiftCreate.tsx b/src/Components/Patient/ShiftCreate.tsx index 7307727d372..c3b01ad189d 100644 --- a/src/Components/Patient/ShiftCreate.tsx +++ b/src/Components/Patient/ShiftCreate.tsx @@ -20,7 +20,6 @@ import { navigate } from "raviger"; import { parsePhoneNumber } from "../../Utils/utils.js"; import { phonePreg } from "../../Common/validation"; import useAppHistory from "../../Common/hooks/useAppHistory"; -import useConfig from "../../Common/hooks/useConfig"; import { useTranslation } from "react-i18next"; import Page from "../Common/components/Page.js"; import Card from "../../CAREUI/display/Card.js"; @@ -30,6 +29,7 @@ import { PhoneNumberValidator } from "../Form/FieldValidators.js"; import useQuery from "../../Utils/request/useQuery.js"; import routes from "../../Redux/api.js"; import request from "../../Utils/request/request.js"; +import careConfig from "@careConfig"; const Loading = lazy(() => import("../Common/Loading")); @@ -44,7 +44,6 @@ export const ShiftCreate = (props: patientShiftProps) => { const [isLoading, setIsLoading] = useState(false); const [patientCategory, setPatientCategory] = useState(); const { t } = useTranslation(); - const { wartime_shifting } = useConfig(); const initForm: any = { shifting_approving_facility: null, @@ -79,7 +78,7 @@ export const ShiftCreate = (props: patientShiftProps) => { }, }; - if (wartime_shifting) { + if (careConfig.wartimeShifting) { requiredFields = { ...requiredFields, shifting_approving_facility: { @@ -200,7 +199,7 @@ export const ShiftCreate = (props: patientShiftProps) => { setIsLoading(true); const data = { - status: wartime_shifting ? "PENDING" : "APPROVED", + status: careConfig.wartimeShifting ? "PENDING" : "APPROVED", origin_facility: props.facilityId, shifting_approving_facility: state.form.shifting_approving_facility?.id, assigned_facility: state.form?.assigned_facility?.id, @@ -282,7 +281,7 @@ export const ShiftCreate = (props: patientShiftProps) => { types={["mobile", "landline"]} /> - {wartime_shifting && ( + {careConfig.wartimeShifting && (
Name of shifting approving facility @@ -336,7 +335,7 @@ export const ShiftCreate = (props: patientShiftProps) => { label="Patient Category" /> - {wartime_shifting && ( + {careConfig.wartimeShifting && ( <> = ({ form, onFormUpdate }) => { - const { enable_scribe } = useConfig(); const [open, setOpen] = useState(false); const [_progress, setProgress] = useState(0); const [stage, setStage] = useState("start"); @@ -545,7 +544,7 @@ export const Scribe: React.FC = ({ form, onFormUpdate }) => { } } - if (!enable_scribe) return null; + if (!careConfig.scribe.enabled) return null; return ( diff --git a/src/Components/Shifting/BoardView.tsx b/src/Components/Shifting/BoardView.tsx index a974e0da79f..f4fb45093aa 100644 --- a/src/Components/Shifting/BoardView.tsx +++ b/src/Components/Shifting/BoardView.tsx @@ -12,7 +12,6 @@ import { downloadShiftRequests } from "../../Redux/actions"; import { formatFilter } from "./Commons"; import { navigate } from "raviger"; -import useConfig from "../../Common/hooks/useConfig"; import useFilters from "../../Common/hooks/useFilters"; import { lazy, useLayoutEffect, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -21,6 +20,7 @@ import ButtonV2 from "../Common/components/ButtonV2"; import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover"; import CareIcon from "../../CAREUI/icons/CareIcon"; import Tabs from "../Common/components/Tabs"; +import careConfig from "@careConfig"; const Loading = lazy(() => import("../Common/Loading")); const PageTitle = lazy(() => import("../Common/PageTitle")); @@ -31,13 +31,12 @@ export default function BoardView() { limit: -1, cacheBlacklist: ["patient_name"], }); - const { wartime_shifting } = useConfig(); - const shiftStatusOptions = wartime_shifting + const shiftStatusOptions = careConfig.wartimeShifting ? SHIFTING_CHOICES_WARTIME : SHIFTING_CHOICES_PEACETIME; - const COMPLETED = wartime_shifting + const COMPLETED = careConfig.wartimeShifting ? [ "COMPLETED", "REJECTED", diff --git a/src/Components/Shifting/ListFilter.tsx b/src/Components/Shifting/ListFilter.tsx index 12bb17db09c..17692ac8f35 100644 --- a/src/Components/Shifting/ListFilter.tsx +++ b/src/Components/Shifting/ListFilter.tsx @@ -17,7 +17,6 @@ import DateRangeFormField from "../Form/FormFields/DateRangeFormField"; import FiltersSlideover from "../../CAREUI/interactive/FiltersSlideover"; import { SelectFormField } from "../Form/FormFields/SelectFormField"; import PhoneNumberFormField from "../Form/FormFields/PhoneNumberFormField"; -import useConfig from "../../Common/hooks/useConfig"; import useMergeState from "../../Common/hooks/useMergeState"; import { useTranslation } from "react-i18next"; @@ -26,18 +25,20 @@ import { dateQueryString, parsePhoneNumber } from "../../Utils/utils"; import dayjs from "dayjs"; import useQuery from "../../Utils/request/useQuery"; import routes from "../../Redux/api"; +import careConfig from "@careConfig"; const getDate = (value: any) => value && dayjs(value).isValid() && dayjs(value).toDate(); export default function ListFilter(props: any) { - const { kasp_enabled, kasp_string, wartime_shifting } = useConfig(); const { filter, onChange, closeFilter, removeFilters } = props; const { t } = useTranslation(); const shiftStatusOptions = ( - wartime_shifting ? SHIFTING_CHOICES_WARTIME : SHIFTING_CHOICES_PEACETIME + careConfig.wartimeShifting + ? SHIFTING_CHOICES_WARTIME + : SHIFTING_CHOICES_PEACETIME ).map((option) => option.text); const [filterState, setFilterState] = useMergeState({ @@ -227,7 +228,7 @@ export default function ListFilter(props: any) {

- {wartime_shifting && ( + {careConfig.wartimeShifting && (
{t("shifting_approving_facility")}
@@ -308,12 +309,12 @@ export default function ListFilter(props: any) { errorClassName="hidden" /> - {kasp_enabled && ( + {careConfig.kasp.enabled && ( option} diff --git a/src/Components/Shifting/ListView.tsx b/src/Components/Shifting/ListView.tsx index cfffcef7377..b05795f934d 100644 --- a/src/Components/Shifting/ListView.tsx +++ b/src/Components/Shifting/ListView.tsx @@ -11,11 +11,7 @@ import SearchInput from "../Form/SearchInput"; import { formatDateTime, formatPatientAge } from "../../Utils/utils"; import { formatFilter } from "./Commons"; import { navigate } from "raviger"; - -import useConfig from "../../Common/hooks/useConfig"; - import useFilters from "../../Common/hooks/useFilters"; - import { useTranslation } from "react-i18next"; import { AdvancedFilterButton } from "../../CAREUI/interactive/FiltersSlideover"; import CareIcon from "../../CAREUI/icons/CareIcon"; @@ -24,11 +20,11 @@ import useAuthUser from "../../Common/hooks/useAuthUser"; import request from "../../Utils/request/request"; import routes from "../../Redux/api"; import useQuery from "../../Utils/request/useQuery"; +import careConfig from "@careConfig"; const Loading = lazy(() => import("../Common/Loading")); export default function ListView() { - const { wartime_shifting } = useConfig(); const { qParams, updateQuery, @@ -127,7 +123,7 @@ export default function ListView() {
- {wartime_shifting && ( + {careConfig.wartimeShifting && (
import("../Common/Loading")); export default function ShiftDetails(props: { id: string }) { - const { header_logo, kasp_full_string, wartime_shifting, kasp_enabled } = - useConfig(); - const [isPrintMode, setIsPrintMode] = useState(false); const [isCopied, setIsCopied] = useState(false); const [openDeleteShiftDialog, setOpenDeleteShiftDialog] = useState(false); const { t } = useTranslation(); - const shiftStatusOptions = wartime_shifting + const shiftStatusOptions = careConfig.wartimeShifting ? SHIFTING_CHOICES_WARTIME : SHIFTING_CHOICES_PEACETIME; @@ -113,7 +110,7 @@ export default function ShiftDetails(props: { id: string }) { t("reason") + ":" + data?.reason; - if (wartime_shifting) { + if (careConfig.wartimeShifting) { formattedText += t("facility_preference") + ": " + data?.assigned_facility_type + "\n"; } @@ -304,7 +301,9 @@ export default function ShiftDetails(props: { id: string }) { return (
-
{data.is_kasp && logo}
+
+ {data.is_kasp && logo} +
@@ -571,7 +570,7 @@ export default function ShiftDetails(props: { id: string }) { {data?.origin_facility_object?.name || "--"}
- {wartime_shifting && ( + {careConfig.wartimeShifting && (
{t("shifting_approving_facility")}:{" "} @@ -635,10 +634,10 @@ export default function ShiftDetails(props: { id: string }) { data?.patient_object.last_consultation?.category}
- {kasp_enabled && ( + {careConfig.kasp.enabled && (
- {kasp_full_string}:{" "} + {careConfig.kasp.fullString}:{" "} {" "} @@ -646,11 +645,11 @@ export default function ShiftDetails(props: { id: string }) {
)} - {wartime_shifting && ( + {careConfig.wartimeShifting && ( <>
- {kasp_full_string}:{" "} + {careConfig.kasp.fullString}:{" "} {" "} @@ -813,7 +812,7 @@ export default function ShiftDetails(props: { id: string }) { {showFacilityCard(data?.assigned_facility_object)}
)} - {wartime_shifting && ( + {careConfig.wartimeShifting && (

{t("details_of_shifting_approving_facility")} diff --git a/src/Components/Shifting/ShiftDetailsUpdate.tsx b/src/Components/Shifting/ShiftDetailsUpdate.tsx index 37adecaedcd..8e6fb582906 100644 --- a/src/Components/Shifting/ShiftDetailsUpdate.tsx +++ b/src/Components/Shifting/ShiftDetailsUpdate.tsx @@ -11,7 +11,6 @@ import { USER_TYPES, } from "../../Common/constants"; import { Cancel, Submit } from "../Common/components/ButtonV2"; - import { navigate, useQueryParams } from "raviger"; import { lazy, useReducer, useState } from "react"; import { ConsultationModel } from "../Facility/models.js"; @@ -26,8 +25,6 @@ import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; import TextFormField from "../Form/FormFields/TextFormField"; import { parsePhoneNumber } from "../../Utils/utils.js"; import useAppHistory from "../../Common/hooks/useAppHistory"; -import useConfig from "../../Common/hooks/useConfig"; - import { useTranslation } from "react-i18next"; import CircularProgress from "../Common/components/CircularProgress.js"; import Card from "../../CAREUI/display/Card"; @@ -41,6 +38,7 @@ import { IShift } from "./models.js"; import request from "../../Utils/request/request.js"; import { PatientModel } from "../Patient/models.js"; import useAuthUser from "../../Common/hooks/useAuthUser.js"; +import careConfig from "@careConfig"; const Loading = lazy(() => import("../Common/Loading")); @@ -51,9 +49,6 @@ interface patientShiftProps { export const ShiftDetailsUpdate = (props: patientShiftProps) => { const { goBack } = useAppHistory(); const { user_type, home_facility } = useAuthUser(); - - const { kasp_full_string, kasp_enabled, wartime_shifting } = useConfig(); - const [qParams, _] = useQueryParams(); const [isLoading, setIsLoading] = useState(true); @@ -137,7 +132,7 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { }, }; - if (wartime_shifting) { + if (careConfig.wartimeShifting) { requiredFields = { ...requiredFields, shifting_approving_facility_object: { @@ -336,7 +331,7 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { label={t("status")} required options={ - wartime_shifting + careConfig.wartimeShifting ? SHIFTING_CHOICES_WARTIME : SHIFTING_CHOICES_PEACETIME } @@ -355,7 +350,7 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { className="w-full bg-white md:col-span-1 md:leading-5" /> - {wartime_shifting && + {careConfig.wartimeShifting && (assignedUserLoading ? ( ) : ( @@ -369,7 +364,7 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { /> ))} - {wartime_shifting && ( + {careConfig.wartimeShifting && (
{t("name_of_shifting_approving_facility")} @@ -429,11 +424,11 @@ export const ShiftDetailsUpdate = (props: patientShiftProps) => { optionValue={(option) => option.value} /> - {kasp_enabled && ( + {careConfig.kasp.enabled && ( { error={state.errors.patient_category} /> - {wartime_shifting && ( + {careConfig.wartimeShifting && ( <> { - const { wartime_shifting } = useConfig(); const [modalFor, setModalFor] = useState({ externalId: undefined, loading: false, @@ -110,7 +108,7 @@ const ShiftCard = ({ shift, filter }: any) => {

- {wartime_shifting && ( + {careConfig.wartimeShifting && (
{ plausible("pageview"); }); @@ -17,11 +15,11 @@ export default function Plausible() { return (