From dd6820375b25ff6abe22c54883664940cd55274a Mon Sep 17 00:00:00 2001 From: Rithvik Nishad Date: Sun, 12 Jan 2025 10:30:46 +0000 Subject: [PATCH] Remove unused code (#9908) --- src/CAREUI/display/NetworkSignal.tsx | 64 -- src/CAREUI/display/Timeline.tsx | 187 ---- src/CAREUI/interactive/HumanChart.tsx | 91 -- src/CAREUI/interactive/KeyboardShortcut.tsx | 99 -- src/CAREUI/interactive/LegendInput.tsx | 161 ---- src/CAREUI/interactive/ScrollOverlay.tsx | 31 - src/CAREUI/interactive/Switch.tsx | 39 - src/CAREUI/misc/AuthorizedChild.tsx | 36 - src/CAREUI/misc/Fullscreen.tsx | 52 -- src/Routers/routes/ConsultationRoutes.tsx | 9 - src/Utils/AuthorizeFor.tsx | 52 -- src/Utils/permissions.ts | 9 - src/Utils/request/api.tsx | 180 +--- src/Utils/utils.ts | 259 +----- src/common/constants.tsx | 850 +----------------- src/common/utils.tsx | 50 -- src/common/validation.tsx | 41 - src/components/Common/Export.tsx | 91 -- src/components/Common/Menu.tsx | 93 -- src/components/Common/Pagination.tsx | 25 +- .../Common/PatientCategoryBadge.tsx | 19 - src/components/Common/Script.tsx | 33 - src/components/Common/Steps.tsx | 108 --- src/components/Common/Switch.tsx | 59 -- .../Common/UserAutocompleteFormField.tsx | 67 +- src/components/Common/UserDetailsComponet.tsx | 23 - .../components/BinaryChronologicalChart.tsx | 68 -- .../Consultations/components/LinePlot.tsx | 259 ------ .../Consultations/components/ReactEcharts.tsx | 46 - .../components/StackedLinePlot.tsx | 111 --- src/components/Facility/FacilityHome.tsx | 22 +- src/components/Facility/models.tsx | 178 ---- src/components/Files/FileUpload.tsx | 168 ++-- src/components/Form/FieldValidators.tsx | 1 - .../Form/FormFields/Autocomplete.tsx | 56 -- src/components/Form/Utils.ts | 20 - .../Patient/PatientConsentRecordBlock.tsx | 115 --- .../Patient/PatientConsentRecords.tsx | 277 ------ src/components/Users/ConfirmFacilityModal.tsx | 79 -- src/components/Users/ProfileComponents.tsx | 21 - src/components/Users/UnlinkFacilityDialog.tsx | 51 -- src/components/Users/UnlinkSkillDialog.tsx | 43 - src/components/Users/UserFilter.tsx | 162 ---- src/components/Users/UserFormValidations.tsx | 45 - src/components/Users/UserListAndCard.tsx | 29 - src/components/Users/UserSummary.tsx | 25 +- src/components/Users/models.tsx | 39 +- src/hooks/useCanvas.ts | 14 - src/hooks/useFullscreen.ts | 57 -- src/hooks/useIsAuthorized.ts | 8 - src/hooks/useMessageListener.ts | 16 - src/hooks/useRangePagination.ts | 112 --- src/hooks/useVisibility.ts | 44 - src/pages/Facility/Utils.tsx | 12 - 54 files changed, 111 insertions(+), 4695 deletions(-) delete mode 100644 src/CAREUI/display/NetworkSignal.tsx delete mode 100644 src/CAREUI/display/Timeline.tsx delete mode 100644 src/CAREUI/interactive/HumanChart.tsx delete mode 100644 src/CAREUI/interactive/KeyboardShortcut.tsx delete mode 100644 src/CAREUI/interactive/LegendInput.tsx delete mode 100644 src/CAREUI/interactive/ScrollOverlay.tsx delete mode 100644 src/CAREUI/interactive/Switch.tsx delete mode 100644 src/CAREUI/misc/AuthorizedChild.tsx delete mode 100644 src/CAREUI/misc/Fullscreen.tsx delete mode 100644 src/Utils/AuthorizeFor.tsx delete mode 100644 src/components/Common/Menu.tsx delete mode 100644 src/components/Common/PatientCategoryBadge.tsx delete mode 100644 src/components/Common/Script.tsx delete mode 100644 src/components/Common/Steps.tsx delete mode 100644 src/components/Common/Switch.tsx delete mode 100644 src/components/Common/UserDetailsComponet.tsx delete mode 100644 src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx delete mode 100644 src/components/Facility/Consultations/components/LinePlot.tsx delete mode 100644 src/components/Facility/Consultations/components/ReactEcharts.tsx delete mode 100644 src/components/Facility/Consultations/components/StackedLinePlot.tsx delete mode 100644 src/components/Patient/PatientConsentRecordBlock.tsx delete mode 100644 src/components/Patient/PatientConsentRecords.tsx delete mode 100644 src/components/Users/ConfirmFacilityModal.tsx delete mode 100644 src/components/Users/ProfileComponents.tsx delete mode 100644 src/components/Users/UnlinkFacilityDialog.tsx delete mode 100644 src/components/Users/UnlinkSkillDialog.tsx delete mode 100644 src/components/Users/UserFilter.tsx delete mode 100644 src/hooks/useCanvas.ts delete mode 100644 src/hooks/useFullscreen.ts delete mode 100644 src/hooks/useIsAuthorized.ts delete mode 100644 src/hooks/useMessageListener.ts delete mode 100644 src/hooks/useRangePagination.ts delete mode 100644 src/hooks/useVisibility.ts diff --git a/src/CAREUI/display/NetworkSignal.tsx b/src/CAREUI/display/NetworkSignal.tsx deleted file mode 100644 index 6cc212f2f0a..00000000000 --- a/src/CAREUI/display/NetworkSignal.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { classNames } from "@/Utils/utils"; - -interface Props { - /** - * Strength of the signal, from 0 to 3 - * - * undefined: Error - * 0: No signal - * 1: Weak signal - * 2: Medium signal - * 3: Strong signal - */ - strength?: number; - children?: React.ReactNode; -} - -export default function NetworkSignal({ strength, children }: Props) { - return ( -
-
- {strength === undefined ? ( - - ) : ( - Array.from({ length: 3 }, (_, i) => ( -
i ? "bg-current" : "bg-zinc-500/30", - )} - /> - )) - )} - {!!strength && strength < 2 && ( - - )} -
- {children} -
- ); -} diff --git a/src/CAREUI/display/Timeline.tsx b/src/CAREUI/display/Timeline.tsx deleted file mode 100644 index 2b029ff526f..00000000000 --- a/src/CAREUI/display/Timeline.tsx +++ /dev/null @@ -1,187 +0,0 @@ -import { createContext, useContext } from "react"; -import { useTranslation } from "react-i18next"; - -import RecordMeta from "@/CAREUI/display/RecordMeta"; -import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; - -import { UserBareMinimum } from "@/components/Users/models"; - -import { classNames, formatName } from "@/Utils/utils"; - -export interface TimelineEvent { - type: TType; - timestamp: string; - by: UserBareMinimum | undefined; - icon: IconName; - iconStyle?: string; - iconWrapperStyle?: string; - notes?: string | React.ReactNode; - cancelled?: boolean; -} - -interface TimelineProps { - className?: string; - children: React.ReactNode | React.ReactNode[]; - name: string; -} - -const TimelineContext = createContext(""); - -export default function Timeline({ className, children, name }: TimelineProps) { - return ( -
-
    - - {children} - -
-
- ); -} - -interface TimelineNodeProps { - event: TimelineEvent; - title?: React.ReactNode; - /** - * Used to add a suffix to the auto-generated title. Will be ignored if `title` is provided. - */ - titleSuffix?: React.ReactNode; - actions?: React.ReactNode; - className?: string; - children?: React.ReactNode; - name?: string; - isLast: boolean; -} - -export const TimelineNode = (props: TimelineNodeProps) => { - const name = useContext(TimelineContext); - const { t } = useTranslation(); - - return ( -
-
-
-
- -
-
-
- {props.title || ( - -
-

- {props.event.by && ( - - {props.event.by.username.startsWith("asset") - ? t("virtual_nursing_assistant") - : `${formatName(props.event.by)} ${ - props.event.by.user_type && - `(${props.event.by.user_type})` - }`}{" "} - - )} - {props.titleSuffix || ( - <> - {`${props.event.type} the `} - {props.name || name} - - )} -

-
- {props.actions && ( - {props.actions} - )} - -
-
-
- )} -
-
- -
- {props.event.notes} - {props.children} -
-
-
- ); -}; - -interface TimelineNodeTitleProps { - children: React.ReactNode | React.ReactNode[]; - event: TimelineEvent; -} - -export const TimelineNodeTitle = (props: TimelineNodeTitleProps) => { - return ( - <> -
-
- -
- {props.children} -
- - ); -}; - -export const TimelineNodeActions = (props: { - children: React.ReactNode | React.ReactNode[]; -}) => { - return
{props.children}
; -}; - -interface TimelineNodeNotesProps { - children?: React.ReactNode | React.ReactNode[]; - icon?: IconName; -} - -export const TimelineNodeNotes = ({ - children, - icon = "l-notes", -}: TimelineNodeNotesProps) => { - if (!children) { - return; - } - - return ( -
- -
- {children} -
-
- ); -}; diff --git a/src/CAREUI/interactive/HumanChart.tsx b/src/CAREUI/interactive/HumanChart.tsx deleted file mode 100644 index 1f6d87f31b3..00000000000 --- a/src/CAREUI/interactive/HumanChart.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { Fragment } from "react/jsx-runtime"; - -import { HumanBodyPaths, HumanBodyRegion } from "@/common/constants"; - -type Props = { - regionColor: (region: HumanBodyRegion) => string; - regionLabelClassName: (region: HumanBodyRegion) => string; - regionText: (region: HumanBodyRegion) => string; - onPartSelect: (region: HumanBodyRegion) => void; -}; - -export default function HumanBodyChart({ - regionLabelClassName, - regionColor, - regionText, - onPartSelect, -}: Props) { - const getTitle = (text: string) => text.split(/(?=[A-Z])/).join(" "); - - return ( -
- {[HumanBodyPaths.anterior, HumanBodyPaths.posterior].map((paths, i) => ( -
-

- {i === 0 ? "Front" : "Back"} -

-
- {paths.map((path, j) => ( - - ))} -
- - {paths.map((path, j) => { - const value = regionText(path.region); - return ( - - {value && ( - - {value} - - )} - onPartSelect(path.region)} - > - {getTitle(path.region)} - - - ); - })} - -
- ))} -
- ); -} diff --git a/src/CAREUI/interactive/KeyboardShortcut.tsx b/src/CAREUI/interactive/KeyboardShortcut.tsx deleted file mode 100644 index ab825d4aada..00000000000 --- a/src/CAREUI/interactive/KeyboardShortcut.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { Fragment } from "react/jsx-runtime"; -import useKeyboardShortcut from "use-keyboard-shortcut"; - -import { classNames, isAppleDevice } from "../../Utils/utils"; - -interface Props { - children?: React.ReactNode; - shortcut: string[]; - altShortcuts?: string[][]; - onTrigger: () => void; - helpText?: string; - tooltipClassName?: string; -} - -export default function KeyboardShortcut(props: Props) { - useKeyboardShortcut(props.shortcut, props.onTrigger); - - if (!props.children) { - return null; - } - - return ( -
- {props.children} - - {props.helpText && ( - {props.helpText} - )} - {(props.altShortcuts || [props.shortcut]).map((shortcut, idx, arr) => ( - <> - - {shortcut.map((key, idx, keys) => ( - <> - {SHORTCUT_KEY_MAP[key] || key} - {idx !== keys.length - 1 && ( - + - )} - - ))} - - {idx !== arr.length - 1 && ( - - or - - )} - - ))} - -
- ); -} - -const SHORTCUT_KEY_MAP = { - Meta: "⌘", - Shift: "⇧Shift", - Alt: "⌥Alt", - Control: isAppleDevice ? "⌃Ctrl" : "Ctrl", - ArrowUp: "↑", - ArrowDown: "↓", - ArrowLeft: "←", - ArrowRight: "→", -} as Record; - -export function KeyboardShortcutKey(props: { - shortcut: string[]; - useShortKeys?: boolean; -}) { - const { shortcut, useShortKeys } = props; - - return ( -
- {shortcut.map((key, idx, keys) => ( - - - {SHORTCUT_KEY_MAP[key] - ? useShortKeys - ? SHORTCUT_KEY_MAP[key][0] - : SHORTCUT_KEY_MAP[key] - : key} - - {idx !== keys.length - 1 && ( - + - )} - - ))} -
- ); -} diff --git a/src/CAREUI/interactive/LegendInput.tsx b/src/CAREUI/interactive/LegendInput.tsx deleted file mode 100644 index 064eb2ceb0f..00000000000 --- a/src/CAREUI/interactive/LegendInput.tsx +++ /dev/null @@ -1,161 +0,0 @@ -import { RefObject, useEffect, useRef, useState } from "react"; -import { useTranslation } from "react-i18next"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { classNames } from "@/Utils/utils"; - -type InputProps = { - id?: string; - name: string; - type: "TEXT" | "PASSWORD" | "EMAIL" | "NUMBER"; - ref?: RefObject; - label?: string; - legend?: string; - required?: boolean; - placeholder?: string; - value?: string; - onChange?: (e: React.ChangeEvent) => void; - onFocus?: (e: React.FocusEvent) => void; - onBlur?: (e: React.FocusEvent) => void; - onKeyUp?: (e: React.KeyboardEvent) => void; - onKeyDown?: (e: React.KeyboardEvent) => void; - onKeyPress?: (e: React.KeyboardEvent) => void; - disabled?: boolean; - error?: string; - className?: string; - outerClassName?: string; - size?: "small" | "medium" | "large"; - autoComplete?: string; -}; - -export default function LegendInput(props: InputProps) { - /** - * Useful for small input forms. Should only be used in special cases. - */ - const { t } = useTranslation(); - const [showPassword, setShowPassword] = useState(false); - const inputRef = useRef(null); - const ref = props.ref || inputRef; - - const legendRef = useRef(null); - const [focused, setFocused] = useState(false); - - const getAutofill = (element: Element) => { - let hasValue; - try { - hasValue = element.matches(":autofill"); - } catch (err) { - try { - hasValue = element.matches(":-webkit-autofill"); - } catch (er) { - hasValue = false; - } - } - hasValue && setFocused(true); - }; - - const detectAutofill = (element: Element) => { - return new Promise((resolve) => { - setTimeout(() => { - resolve(getAutofill(element)); - }, 600); - }); - }; - - const testAutoFill = async (element: Element) => { - await detectAutofill(element); - }; - - useEffect(() => { - ref.current && testAutoFill(ref.current); - }, [ref.current]); - return ( -
- {props.label && ( - - )} -
- {props.legend && ( - - )} - - { - props.onFocus && props.onFocus(e); - setFocused(true); - }} - onBlur={(e) => { - props.onBlur && props.onBlur(e); - setFocused(false); - }} - onKeyUp={props.onKeyUp} - onKeyDown={props.onKeyDown} - onKeyPress={props.onKeyPress} - disabled={props.disabled} - required={props.required} - autoComplete={props.autoComplete} - className={classNames( - "cui-input w-full rounded-md border-2 border-secondary-300 border-transparent bg-secondary-50 shadow-sm focus:border-2 focus:border-primary-500 focus:bg-secondary-100 focus:outline-none focus:ring-0", - props.size === "small" && "px-3 py-2 text-xs", - (!props.size || !["small", "large"].includes(props.size)) && - "px-4 py-3", - props.size === "large" && "px-5 py-4 text-lg", - props.type === "PASSWORD" && "pr-10", - props.error && "border-red-500", - props.className, - )} - /> - {props.type === "PASSWORD" && ( - - )} -
- {!!props.error && ( -
{t(props.error)}
- )} -
- ); -} diff --git a/src/CAREUI/interactive/ScrollOverlay.tsx b/src/CAREUI/interactive/ScrollOverlay.tsx deleted file mode 100644 index 47d3048e2aa..00000000000 --- a/src/CAREUI/interactive/ScrollOverlay.tsx +++ /dev/null @@ -1,31 +0,0 @@ -import useVisibility from "@/hooks/useVisibility"; - -import { classNames } from "@/Utils/utils"; - -interface Props { - className?: string; - children: React.ReactNode; - overlay: React.ReactNode; - disableOverlay?: boolean; -} - -export default function ScrollOverlay(props: Props) { - const [bottomIsVisible, ref] = useVisibility(); - const hasScrollContent = !props.disableOverlay && !bottomIsVisible; - - return ( -
- {props.children} - -
-
- {hasScrollContent && props.overlay} -
-
- ); -} diff --git a/src/CAREUI/interactive/Switch.tsx b/src/CAREUI/interactive/Switch.tsx deleted file mode 100644 index a30f85fbc14..00000000000 --- a/src/CAREUI/interactive/Switch.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { classNames } from "@/Utils/utils"; - -interface Props { - tabs: Record; - selected: T; - onChange: (tab: T) => void; - size?: "sm" | "md" | "lg"; -} - -export default function Switch({ - size = "sm", - ...props -}: Props) { - return ( -
    - {Object.keys(props.tabs).map((tab) => { - return ( -
  • props.onChange(tab as T)} - > - {props.tabs[tab as T]} -
  • - ); - })} -
- ); -} diff --git a/src/CAREUI/misc/AuthorizedChild.tsx b/src/CAREUI/misc/AuthorizedChild.tsx deleted file mode 100644 index 5805ed5f44c..00000000000 --- a/src/CAREUI/misc/AuthorizedChild.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { ReactNode } from "react"; - -import useAuthUser from "@/hooks/useAuthUser"; -import { useIsAuthorized } from "@/hooks/useIsAuthorized"; -import useSlug from "@/hooks/useSlug"; - -import { AuthorizedForCB } from "@/Utils/AuthorizeFor"; - -interface Props { - children: (value: { isAuthorized: boolean }) => JSX.Element; - authorizeFor: AuthorizedForCB; -} - -const AuthorizedChild = (props: Props) => { - const isAuthorized = useIsAuthorized(props.authorizeFor); - return props.children({ isAuthorized }); -}; - -export default AuthorizedChild; - -export const AuthorizedForConsultationRelatedActions = (props: { - children: ReactNode; -}) => { - const me = useAuthUser(); - const facilityId = useSlug("facility"); - - if ( - me.home_facility_object?.id === facilityId || - me.user_type === "DistrictAdmin" || - me.user_type === "StateAdmin" - ) { - return props.children; - } - - return null; -}; diff --git a/src/CAREUI/misc/Fullscreen.tsx b/src/CAREUI/misc/Fullscreen.tsx deleted file mode 100644 index 82c6d9e91ed..00000000000 --- a/src/CAREUI/misc/Fullscreen.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { useEffect, useRef } from "react"; - -interface Props { - className?: string; - fullscreenClassName?: string; - children: React.ReactNode; - fullscreen: boolean; - onExit: (reason?: "DEVICE_UNSUPPORTED") => void; -} - -export default function Fullscreen(props: Props) { - const ref = useRef(null); - - useEffect(() => { - if (!ref.current) { - return; - } - - if (props.fullscreen) { - if (ref.current.requestFullscreen) { - ref.current.requestFullscreen(); - } else { - props.onExit("DEVICE_UNSUPPORTED"); - } - } else { - document.exitFullscreen?.(); - } - }, [props.fullscreen]); - - useEffect(() => { - const listener = () => { - if (!document.fullscreenElement) { - props.onExit(); - } - }; - - document.addEventListener("fullscreenchange", listener); - - return () => { - document.removeEventListener("fullscreenchange", listener); - }; - }, [props.onExit]); - - return ( -
- {props.children} -
- ); -} diff --git a/src/Routers/routes/ConsultationRoutes.tsx b/src/Routers/routes/ConsultationRoutes.tsx index 96812b5ea40..6ae459d7f7a 100644 --- a/src/Routers/routes/ConsultationRoutes.tsx +++ b/src/Routers/routes/ConsultationRoutes.tsx @@ -1,7 +1,6 @@ import QuestionnaireResponseView from "@/components/Facility/ConsultationDetails/QuestionnaireResponseView"; import EncounterQuestionnaire from "@/components/Patient/EncounterQuestionnaire"; import FileUploadPage from "@/components/Patient/FileUploadPage"; -import PatientConsentRecords from "@/components/Patient/PatientConsentRecords"; import { AppRoutes } from "@/Routers/AppRouter"; import { EncounterShow } from "@/pages/Encounters/EncounterShow"; @@ -73,14 +72,6 @@ const consultationRoutes: AppRoutes = { ({ patientId, id }) => ( ), - "/facility/:facilityId/patient/:patientId/consultation/:id/consent-records": - ({ facilityId, patientId, id }) => ( - - ), "/facility/:facilityId/patient/:patientId/encounterId/:id/files/": ({ facilityId, patientId, diff --git a/src/Utils/AuthorizeFor.tsx b/src/Utils/AuthorizeFor.tsx deleted file mode 100644 index e55ab410232..00000000000 --- a/src/Utils/AuthorizeFor.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from "react"; - -import ErrorPage from "@/components/ErrorPages/DefaultErrorPage"; - -import useAuthUser from "@/hooks/useAuthUser"; - -import { UserRole } from "@/common/constants"; - -export type AuthorizedForCB = (userType: UserRole) => boolean; - -interface AuthorizeUserRouteProps { - userTypes: UserRole[]; - children: React.ReactNode; -} - -export type AuthorizedElementProps = { - /** - * Restrict access of this button to specific roles. - * - * **Example:** - * ```jsx - * !role.includes('ReadOnly')}> - * Delete Facility - * - * - * Delete Facility - * - * ``` - */ - authorizeFor?: AuthorizedForCB | undefined; -}; - -export const NonReadOnlyUsers = (userType: UserRole) => - !userType.includes("ReadOnly"); - -export const Anyone = () => true; - -export default function (userTypes: UserRole[]) { - return (userType: UserRole) => userTypes.includes(userType); -} - -export const AuthorizeUserRoute: React.FC = ({ - userTypes, - children, -}) => { - const authUser = useAuthUser(); - if (userTypes.includes(authUser.user_type)) { - return <>{children}; - } else { - return ; - } -}; diff --git a/src/Utils/permissions.ts b/src/Utils/permissions.ts index fdeb7b0fa69..b8b521c758b 100644 --- a/src/Utils/permissions.ts +++ b/src/Utils/permissions.ts @@ -1,7 +1,5 @@ import { UserModel } from "@/components/Users/models"; -import { UserRole } from "@/common/constants"; - import { UserBase } from "@/types/user/user"; // To do: Rewrite to check if belongs to same org and in higher @@ -62,10 +60,3 @@ export const editUserPermissions = ( // To do: check above //return checkIfStateOrDistrictAdminInSameLocation(authUser, targetUser); }; - -export const CameraFeedPermittedUserTypes: UserRole[] = [ - "DistrictAdmin", - "StateAdmin", - "StateReadOnlyAdmin", - "Doctor", -]; diff --git a/src/Utils/request/api.tsx b/src/Utils/request/api.tsx index 940e5c383dc..88468e2cf79 100644 --- a/src/Utils/request/api.tsx +++ b/src/Utils/request/api.tsx @@ -2,8 +2,6 @@ import { CommentModel, FacilityModel, FacilityRequest, - IUserFacilityRequest, - PatientConsentModel, } from "@/components/Facility/models"; import { CreateFileRequest, @@ -11,7 +9,6 @@ import { FileUploadModel, } from "@/components/Patient/models"; import { - SkillModel, UpdatePasswordForm, UserAssignedModel, UserModel, @@ -23,7 +20,6 @@ import { AppointmentPatientRegister, } from "@/pages/Patient/Utils"; import { Encounter, EncounterEditRequest } from "@/types/emr/encounter"; -import { MedicationAdministration } from "@/types/emr/medicationAdministration"; import { MedicationRequest } from "@/types/emr/medicationRequest"; import { MedicationStatement } from "@/types/emr/medicationStatement"; import { PartialPatientModel, Patient } from "@/types/emr/newPatient"; @@ -88,7 +84,7 @@ export enum HttpMethod { DELETE = "DELETE", } -export const API = ( +export const API = ( route: `${HttpMethod} ${string}`, ) => { const [method, path] = route.split(" ") as [HttpMethod, string]; @@ -100,23 +96,6 @@ export const API = ( }; }; -export const ModelCrudApis = < - TModel extends object, - TCreate = TModel, - TListResponse = TModel, - TUpdate = TModel, ->( - route: string, -) => { - return { - list: API>(`GET ${route}/`), - create: API(`POST ${route}/`), - retrieve: API(`GET ${route}/{id}/`), - update: API(`PUT ${route}/{id}/`), - delete: API(`DELETE ${route}/{id}/`), - }; -}; - const routes = { // Auth Endpoints login: { @@ -188,82 +167,6 @@ const routes = { TRes: Type>(), }, - getUserList: { - path: "/api/v1/users/", - method: "GET", - TRes: Type>(), - }, - - userListSkill: { - path: "/api/v1/users/{username}/skill/", - method: "GET", - TRes: Type>(), - }, - - userListFacility: { - path: "/api/v1/users/{username}/get_facilities/", - method: "GET", - TRes: Type>(), - }, - - addUserFacility: { - path: "/api/v1/users/{username}/add_facility/", - method: "PUT", - TBody: Type(), - TRes: Type(), - }, - - addUserSkill: { - path: "/api/v1/users/{username}/skill/", - method: "POST", - TBody: Type<{ skill: string }>(), - TRes: Type(), - }, - - deleteUserFacility: { - path: "/api/v1/users/{username}/delete_facility/", - method: "DELETE", - TBody: Type(), - TRes: Type>(), - }, - - clearHomeFacility: { - path: "/api/v1/users/{username}/clear_home_facility/", - method: "DELETE", - TRes: Type>(), - }, - - deleteUserSkill: { - path: "/api/v1/users/{username}/skill/{id}/", - method: "DELETE", - TRes: Type>(), - }, - - createUser: { - path: "/api/v1/users/", - method: "POST", - noAuth: true, - }, - - updateUser: { - path: "/api/v1/users/", - method: "PUT", - }, - - partialUpdateUser: { - path: "/api/v1/users/{username}/", - method: "PATCH", - TRes: Type(), - TBody: Type>(), - }, - - updateProfilePicture: { - path: "/api/v1/users/{username}/profile_picture/", - method: "PATCH", - TRes: Type(), - TBody: Type<{ profile_picture_url: string }>(), - }, - deleteProfilePicture: { path: "/api/v1/users/{username}/profile_picture/", method: "DELETE", @@ -276,20 +179,6 @@ const routes = { TRes: Type>(), }, - addUser: { - path: "/api/v1/users/", - method: "POST", - TRes: Type(), - }, - - searchUser: { - path: "/api/v1/users/", - }, - - getOnlineDoctors: { - path: "/api/v1/users/?user_type=Doctor&ordering=-last_login", - }, - // Facility Endpoints getPermittedFacilities: { @@ -328,13 +217,6 @@ const routes = { TBody: Type(), }, - partialUpdateFacility: { - path: "/api/v1/facility/{id}/", - method: "PATCH", - TRes: Type(), - TBody: Type>(), - }, - deleteFacilityCoverImage: { path: "/api/v1/facility/{id}/cover_image/", method: "DELETE", @@ -370,11 +252,7 @@ const routes = { method: "POST", TRes: Type>(), }, - patientList: { - path: "/api/v1/patient/", - method: "GET", - TRes: Type>(), - }, + addPatient: { path: "/api/v1/patient/", method: "POST", @@ -391,36 +269,6 @@ const routes = { method: "PUT", TRes: Type(), }, - patchPatient: { - path: "/api/v1/patient/{id}/", - method: "PATCH", - TBody: Type>(), - TRes: Type(), - }, - - // Consents - listConsents: { - path: "/api/v1/consultation/{consultationId}/consents/", - method: "GET", - TRes: Type>(), - }, - getConsent: { - path: "/api/v1/consultation/{consultationId}/consents/{id}/", - method: "GET", - TRes: Type(), - }, - createConsent: { - path: "/api/v1/consultation/{consultationId}/consents/", - method: "POST", - TRes: Type(), - TBody: Type>(), - }, - partialUpdateConsent: { - path: "/api/v1/consultation/{consultationId}/consents/{id}/", - method: "PATCH", - TRes: Type(), - TBody: Type>(), - }, //Profile @@ -429,10 +277,6 @@ const routes = { method: "GET", TRes: Type(), }, - updateUserDetails: { - path: "/api/v1/users/", - method: "PUT", - }, // FileUpload Create createUpload: { @@ -813,26 +657,6 @@ const routes = { method: "GET", TRes: Type>(), }, - discontinue: { - path: "/api/v1/patient/{patientId}/medication/request/{id}/discontinue/", - method: "POST", - TBody: Type<{ status_reason: MedicationRequest["status_reason"] }>(), - TRes: Type(), - }, - }, - - medicationAdministration: { - list: { - path: "/api/v1/patient/{patientId}/medication/administration/", - method: "GET", - TRes: Type>(), - }, - create: { - path: "/api/v1/patient/{patientId}/medication/administration/", - method: "POST", - TBody: Type(), - TRes: Type(), - }, }, medicationStatement: { diff --git a/src/Utils/utils.ts b/src/Utils/utils.ts index 0d918a4398e..5b6af0ac5a6 100644 --- a/src/Utils/utils.ts +++ b/src/Utils/utils.ts @@ -1,84 +1,15 @@ import { differenceInMinutes, format } from "date-fns"; import html2canvas from "html2canvas"; -import { t } from "i18next"; -import { toast } from "sonner"; import { AREACODES, IN_LANDLINE_AREA_CODES } from "@/common/constants"; import phoneCodesJson from "@/common/static/countryPhoneAndFlags.json"; import dayjs from "@/Utils/dayjs"; import { Time } from "@/Utils/types"; -import { DoseRange, Timing } from "@/types/emr/medicationRequest"; import { Patient } from "@/types/emr/newPatient"; import { PatientModel } from "@/types/emr/patient"; -import { Code } from "@/types/questionnaire/code"; import { Quantity } from "@/types/questionnaire/quantity"; -interface ApacheParams { - age: number; - organFailure: boolean; - temperatureC: number; - heartRate: number; - respiratoryRate: number; - sodium: number; - potassium: number; - creatinine: number; - acuteRenalFailure: boolean; - hematocrit: number; - wbcCount: number; - glasgowComaScore: number; - fiO2: number; -} - -export const calculateApache2Score = (apacheParams: ApacheParams): number => { - const { - age, - organFailure, - temperatureC, - heartRate, - respiratoryRate, - sodium, - potassium, - creatinine, - acuteRenalFailure, - hematocrit, - wbcCount, - glasgowComaScore, - fiO2, - } = apacheParams; - - const ageScore = age < 65 ? 1 : 0; - const organFailureScore = organFailure ? 1 : 0; - const temperatureScore = temperatureC < 37.5 ? 1 : 0; - const heartRateScore = heartRate < 60 ? 1 : 0; - const respiratoryRateScore = respiratoryRate < 12 ? 1 : 0; - const sodiumScore = sodium < 135 ? 1 : 0; - const potassiumScore = potassium < 3.5 ? 1 : 0; - const creatinineScore = creatinine < 0.7 ? 1 : 0; - const acuteRenalFailureScore = acuteRenalFailure ? 1 : 0; - const hematocritScore = hematocrit < 0.45 ? 1 : 0; - const wbcCountScore = wbcCount < 10 ? 1 : 0; - const glasgowComaScoreScore = glasgowComaScore < 6 ? 1 : 0; - const fiO2Score = fiO2 < 0.7 ? 1 : 0; - - const totalScore = - ageScore + - organFailureScore + - temperatureScore + - heartRateScore + - respiratoryRateScore + - sodiumScore + - potassiumScore + - creatinineScore + - acuteRenalFailureScore + - hematocritScore + - wbcCountScore + - glasgowComaScoreScore + - fiO2Score; - - return totalScore; -}; - const DATE_FORMAT = "DD/MM/YYYY"; const TIME_FORMAT = "hh:mm A"; const DATE_TIME_FORMAT = `${TIME_FORMAT}; ${DATE_FORMAT}`; @@ -100,16 +31,10 @@ export const formatDateTime = (date: DateLike, format?: string) => { return obj.format(DATE_TIME_FORMAT); }; -export const formatDate = (date: DateLike, format = DATE_FORMAT) => - formatDateTime(date, format); - export const formatTimeShort = (time: Time) => { return format(new Date(`1970-01-01T${time}`), "h:mm a").replace(":00", ""); }; -export const formatTime = (date: DateLike, format = TIME_FORMAT) => - formatDateTime(date, format); - export const relativeDate = (date: DateLike, withoutSuffix = false) => { const obj = dayjs(date); return `${obj.fromNow(withoutSuffix)}${ @@ -167,11 +92,6 @@ function _isAppleDevice() { */ export const isAppleDevice = _isAppleDevice(); -/** - * `true` if device is an iOS device, else `false` - */ -export const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); - /** * Conditionally concatenate classes. An alternate replacement for `clsx`. * @@ -180,6 +100,8 @@ export const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent); *
* // "md:flex p-0" * ``` + * + * @deprecated Use `cn` from `@/lib/utils` instead. */ export const classNames = (...classes: (string | boolean | undefined)[]) => { return classes.filter(Boolean).join(" "); @@ -193,42 +115,6 @@ export const getPincodeDetails = async (pincode: string, apiKey: string) => { return data.records[0]; }; -export const includesIgnoreCase = (str1: string, str2: string) => { - if (!str1 || !str2) return false; - const lowerCaseStr1 = str1.toLowerCase(); - const lowerCaseStr2 = str2.toLowerCase(); - return ( - lowerCaseStr1.includes(lowerCaseStr2) || - lowerCaseStr2.includes(lowerCaseStr1) - ); -}; - -export const getExperienceSuffix = (date?: Date) => { - if (!date) return "0 Years"; - - const today = new Date(); - - let m = (today.getFullYear() - date.getFullYear()) * 12; - m -= date.getMonth(); - m += today.getMonth(); - - let str = ""; - - const years = Math.floor(m / 12); - const months = m % 12; - - if (years) str += `${years} years `; - if (months) str += `${months} months`; - - return str; -}; - -export const formatCurrency = (price: number) => - price?.toLocaleString("en-IN", { - style: "currency", - currency: "INR", - }); - export const isUserOnline = (user: { last_login: DateLike }) => { return user.last_login ? dayjs().subtract(5, "minutes").isBefore(user.last_login) @@ -378,20 +264,6 @@ const getRelativeDateSuffix = (abbreviated: boolean) => { }; }; -export const patientAgeInYears = (obj: PatientModel) => { - const start = dayjs( - obj.date_of_birth - ? new Date(obj.date_of_birth) - : new Date(obj.year_of_birth!, 0, 1), - ); - - const end = dayjs( - obj.death_datetime ? new Date(obj.death_datetime) : new Date(), - ); - - return end.diff(start, "years"); -}; - export const formatPatientAge = ( obj: PatientModel | Patient, abbreviated = false, @@ -429,29 +301,6 @@ export const formatPatientAge = ( return `${day}${suffixes.day}`; }; -export const compareBy = (key: keyof T) => { - return (a: T, b: T) => { - return a[key] < b[key] ? -1 : a[key] > b[key] ? 1 : 0; - }; -}; - -export const compareByDateString = (key: keyof T) => { - return (a: T, b: T) => { - const aV = new Date(a[key] as string); - const bV = new Date(b[key] as string); - return aV < bV ? -1 : aV > bV ? 1 : 0; - }; -}; - -export const isValidUrl = (url?: string) => { - try { - new URL(url ?? ""); - return true; - } catch { - return false; - } -}; - export const mergeQueryOptions = ( selected: T[], queryOptions: T[], @@ -466,13 +315,6 @@ export const mergeQueryOptions = ( ]; }; -export const properRoundOf = (value: number) => { - if (value % 1 === 0) { - return value.toFixed(); - } - return value.toFixed(2); -}; - /** * A utility method to format an array of string to human readable format. * @@ -492,58 +334,6 @@ export const humanizeStrings = (strings: readonly string[], empty = "") => { return `${items.reverse().join(", ")} and ${last}`; }; -export type ValueDescription = { - till?: number; - text: React.ReactNode; - className?: string; - color?: string; -}; - -export const getValueDescription = ( - valueDescriptions: ValueDescription[], - value: number, -) => { - return valueDescriptions.find((vd) => (vd.till || 0) >= (value || 0)); -}; - -export const rangeValueDescription = (range: { - low?: number; - high?: number; -}) => { - const results: ValueDescription[] = []; - - if (range.low != null) { - results.push({ - till: range.low, - text: "Low", - className: "text-red-500", - }); - } - - results.push({ - till: range.high, - text: "Normal", - className: "text-green-500", - }); - - if (range.high != null) { - results.push({ - text: "High", - className: "text-red-500", - }); - } - - return results; -}; - -export const celsiusToFahrenheit = (celsius: number) => { - return (celsius * 9) / 5 + 32; -}; - -export const fahrenheitToCelsius = (fahrenheit: number) => { - return ((fahrenheit - 32) * 5) / 9; -}; - /** * Although same as `Objects.keys(...)`, this provides better type-safety. */ @@ -551,21 +341,6 @@ export const keysOf = (obj: T) => { return Object.keys(obj) as (keyof T)[]; }; -// Utility to check if a value is "empty" -export const isEmpty = (value: unknown) => { - return value === "" || value == undefined; -}; - -// equivalent to lodash omitBy -export function omitBy>( - obj: T, - predicate: (value: unknown) => boolean, -): Partial { - return Object.fromEntries( - Object.entries(obj).filter(([_, value]) => !predicate(value)), - ) as Partial; -} - export const properCase = (str: string) => { return str .split("_") @@ -580,33 +355,12 @@ export const getMonthStartAndEnd = (date: Date) => { }; }; -export const displayCode = (code?: Code) => { - if (!code) return "N/A"; - - return code.display ?? code.code; -}; - export const displayQuantity = (quantity?: Quantity) => { if (!quantity) return "N/A"; return [quantity.value ?? "N/A", quantity.unit].join(" "); }; -// TODO: make it generic -export const displayDoseRange = (range?: DoseRange) => { - if (!range) return "N/A"; - - return ([range.low, range.high] as Quantity[]) - .map(displayQuantity) - .join(" - "); -}; - -export const displayTiming = (timing?: Timing) => { - if (!timing || !timing.repeat) return "N/A"; - - return `${timing.repeat.frequency} every ${timing.repeat.period} ${timing.repeat.period_unit}`; -}; - /** * Returns hours and minutes between two dates. * @@ -642,15 +396,6 @@ export const saveElementAsImage = async (id: string, filename: string) => { link.click(); }; -export const copyToClipboard = async (content: string) => { - try { - await navigator.clipboard.writeText(content); - toast.success(t("copied_to_clipboard")); - } catch (err) { - toast.error(t("copying_is_not_allowed")); - } -}; - export const conditionalAttribute = ( condition: boolean, attributes: Record, diff --git a/src/common/constants.tsx b/src/common/constants.tsx index 64af8d373ac..a88b50e35b0 100644 --- a/src/common/constants.tsx +++ b/src/common/constants.tsx @@ -1,15 +1,6 @@ import { IconName } from "@/CAREUI/icons/CareIcon"; -import { SortOption } from "@/components/Common/SortDropdown"; -import { PatientCategory } from "@/components/Facility/models"; -import { PhoneNumberValidator } from "@/components/Form/FieldValidators"; - -import { dateQueryString } from "@/Utils/utils"; - -import { SchemaType } from "./schemaParser"; - export const RESULTS_PER_PAGE_LIMIT = 14; -export const PAGINATION_LIMIT = 36; /** * Contains local storage keys that are potentially used in multiple places. @@ -19,6 +10,7 @@ export const LocalStorageKeys = { refreshToken: "care_refresh_token", patientTokenKey: "care_patient_token", }; + export interface OptionsType { id: number | string; text: string; @@ -27,77 +19,6 @@ export interface OptionsType { disabled?: boolean; } -export const USER_TYPE_OPTIONS = [ - { id: "Pharmacist", role: "Pharmacist", readOnly: false }, - { id: "Volunteer", role: "Volunteer", readOnly: false }, - { id: "StaffReadOnly", role: "Staff", readOnly: true }, - { id: "Staff", role: "Staff", readOnly: false }, - // { id: "NurseReadOnly", role: "Nurse", readOnly: true }, - { id: "Nurse", role: "Nurse", readOnly: false }, - { id: "Doctor", role: "Doctor", readOnly: false }, - { id: "WardAdmin", role: "Ward Admin", readOnly: false }, - { id: "LocalBodyAdmin", role: "Local Body Admin", readOnly: false }, - { id: "DistrictLabAdmin", role: "District Lab Admin", readOnly: false }, - { id: "DistrictReadOnlyAdmin", role: "District Admin", readOnly: true }, - { id: "DistrictAdmin", role: "District Admin", readOnly: false }, - { id: "StateLabAdmin", role: "State Lab Admin", readOnly: false }, - { id: "StateReadOnlyAdmin", role: "State Admin", readOnly: true }, - { id: "StateAdmin", role: "State Admin", readOnly: false }, -] as const; - -export const USER_LAST_ACTIVE_OPTIONS = [ - { id: 1, text: "24 hours" }, - { id: 7, text: "7 days" }, - { id: 30, text: "30 days" }, - { id: 90, text: "90 days" }, - { id: 365, text: "1 Year" }, - { id: "never", text: "Never" }, -]; - -export type UserRole = (typeof USER_TYPE_OPTIONS)[number]["id"]; - -export const USER_TYPES = USER_TYPE_OPTIONS.map((o) => o.id); - -export const DOWNLOAD_TYPES: Array = [ - "Facility List", - "Facility Capacity List", - "Facility Doctors List", - "Facility Triage Data", -]; - -export const TEST_TYPE_CHOICES: Array = [ - { id: 10, text: "UNK" }, - { id: 20, text: "ANTIGEN" }, - { id: 30, text: "RTPCR" }, - { id: 40, text: "CBNAAT" }, - { id: 50, text: "TRUENAT" }, - { id: 60, text: "RTLAMP" }, - { id: 70, text: "POCPCR" }, -]; - -export const DISTRICT_CHOICES: Array = [ - { id: 1, text: "Thiruvananthapuram" }, - { id: 2, text: "Kollam" }, - { id: 3, text: "Pathanamthitta" }, - { id: 4, text: "Alappuzha" }, - { id: 5, text: "Kottayam" }, - { id: 6, text: "Idukki" }, - { id: 7, text: "Ernakulam" }, - { id: 8, text: "Thrissur" }, - { id: 9, text: "Palakkad" }, - { id: 10, text: "Malappuram" }, - { id: 11, text: "Kozhikode" }, - { id: 12, text: "Wayanad" }, - { id: 13, text: "Kannur" }, - { id: 14, text: "Kasaragod" }, -]; - -export const VEHICLE_TYPES: Array = [ - { id: 1, text: "Basic" }, - { id: 2, text: "Cardiac" }, - { id: 3, text: "Hearse" }, -]; - export const FACILITY_TYPES: Array = [ // { id: 1, text: "Educational Inst" }, // { id: 4, text: "Hostel" }, @@ -140,40 +61,6 @@ export const FACILITY_TYPES: Array = [ { id: 4000, text: "Community Based Organization" }, ]; -export const SHIFTING_CHOICES_WARTIME: Array = [ - { id: 10, text: "PENDING", label: "SHIFTING APPROVAL PENDING" }, - { id: 15, text: "ON HOLD" }, - { id: 20, text: "APPROVED" }, - { id: 30, text: "REJECTED" }, - { id: 40, text: "DESTINATION APPROVED" }, - { id: 50, text: "DESTINATION REJECTED" }, - { id: 55, text: "TRANSPORTATION TO BE ARRANGED" }, - { id: 60, text: "PATIENT TO BE PICKED UP" }, - { id: 70, text: "TRANSFER IN PROGRESS" }, - { id: 80, text: "COMPLETED" }, - { id: 90, text: "PATIENT EXPIRED" }, - { id: 100, text: "CANCELLED" }, -]; - -export const SHIFTING_CHOICES_PEACETIME: Array = [ - { id: 20, text: "APPROVED", label: "PATIENTS TO BE SHIFTED" }, - { id: 40, text: "DESTINATION APPROVED" }, - // { id: 50, text: "DESTINATION REJECTED" }, - { id: 60, text: "PATIENT TO BE PICKED UP", label: "TRANSPORTATION ARRANGED" }, - { id: 70, text: "TRANSFER IN PROGRESS" }, - { id: 80, text: "COMPLETED" }, - { id: 90, text: "PATIENT EXPIRED" }, - { id: 100, text: "CANCELLED" }, -]; - -export const SHIFTING_VEHICLE_CHOICES: Array = [ - { id: 10, text: "D Level Ambulance" }, - { id: 20, text: "All double chambered Ambulance with EMT" }, - { id: 30, text: "Ambulance without EMT" }, - { id: 50, text: "Car" }, - { id: 50, text: "Auto-rickshaw" }, -]; - export const SHIFTING_FILTER_ORDER: Array = [ { id: 1, text: "created_date", desc: "ASC Created Date" }, { id: 2, text: "-created_date", desc: "DESC Created Date" }, @@ -181,145 +68,6 @@ export const SHIFTING_FILTER_ORDER: Array = [ { id: 4, text: "-modified_date", desc: "DESC Modified Date" }, ]; -export const PATIENT_SORT_OPTIONS: SortOption[] = [ - { isAscending: false, value: "-created_date" }, - { isAscending: true, value: "created_date" }, - { isAscending: false, value: "-category_severity" }, - { isAscending: true, value: "category_severity" }, - { isAscending: false, value: "-modified_date" }, - { isAscending: true, value: "modified_date" }, - { - isAscending: true, - value: "facility__name,last_consultation__current_bed__bed__name", - }, - { - isAscending: false, - value: "facility__name,-last_consultation__current_bed__bed__name", - }, - { isAscending: false, value: "-review_time" }, - { isAscending: true, value: "review_time" }, - { isAscending: true, value: "name" }, - { isAscending: false, value: "-name" }, -]; - -export const EVENTS_SORT_OPTIONS: SortOption[] = [ - { isAscending: false, value: "-created_date" }, - { isAscending: true, value: "created_date" }, - { isAscending: false, value: "-taken_at" }, - { isAscending: true, value: "taken_at" }, -]; - -export const DISCHARGED_PATIENT_SORT_OPTIONS: SortOption[] = [ - { isAscending: false, value: "-created_date" }, - { isAscending: true, value: "created_date" }, - { isAscending: false, value: "-modified_date" }, - { isAscending: true, value: "modified_date" }, - { isAscending: true, value: "name" }, - { isAscending: false, value: "-name" }, -]; - -export const BED_TYPES = [100, 200, 300, 400, 500]; - -export const DOCTOR_SPECIALIZATION: Array = [ - { id: 1, text: "General Medicine" }, - { id: 2, text: "Pulmonology" }, - { id: 3, text: "Intensivist" }, - { id: 4, text: "Pediatrician" }, - { id: 6, text: "Anesthesiologist" }, - { id: 7, text: "Cardiac Surgeon" }, - { id: 8, text: "Cardiologist" }, - { id: 9, text: "Dentist" }, - { id: 10, text: "Dermatologist" }, - { id: 11, text: "Diabetologist" }, - { id: 12, text: "Emergency Medicine Physician" }, - { id: 13, text: "Endocrinologist" }, - { id: 14, text: "Family Physician" }, - { id: 15, text: "Gastroenterologist" }, - { id: 16, text: "General Surgeon" }, - { id: 17, text: "Geriatrician" }, - { id: 18, text: "Hematologist" }, - { id: 19, text: "Immunologist" }, - { id: 20, text: "Infectious Disease Specialist" }, - { id: 21, text: "MBBS doctor" }, - { id: 22, text: "Medical Officer" }, - { id: 23, text: "Nephrologist" }, - { id: 24, text: "Neuro Surgeon" }, - { id: 25, text: "Neurologist" }, - { id: 26, text: "Obstetrician and Gynecologist" }, - { id: 27, text: "Oncologist" }, - { id: 28, text: "Oncology Surgeon" }, - { id: 29, text: "Ophthalmologist" }, - { - id: 30, - text: "Oral and Maxillofacial Surgeon", - }, - { id: 31, text: "Orthopedic" }, - { id: 32, text: "Orthopedic Surgeon" }, - { id: 33, text: "Otolaryngologist (ENT)" }, - { id: 34, text: "Palliative care Physician" }, - { id: 35, text: "Pathologist" }, - { id: 36, text: "Pediatric Surgeon" }, - { id: 37, text: "Physician" }, - { id: 38, text: "Plastic Surgeon" }, - { id: 39, text: "Psychiatrist" }, - { id: 40, text: "Pulmonologist" }, - { id: 41, text: "Radio technician" }, - { id: 42, text: "Radiologist" }, - { id: 43, text: "Rheumatologist" }, - { id: 44, text: "Sports Medicine Specialist" }, - { id: 45, text: "Thoraco-Vascular Surgeon" }, - { - id: 46, - text: "Transfusion Medicine Specialist", - }, - { id: 47, text: "Urologist" }, - { id: 48, text: "Nurse" }, - { id: 5, text: "Others" }, -]; - -export const MEDICAL_HISTORY_CHOICES: Array = [ - { id: 1, text: "NO" }, - { id: 2, text: "Diabetes" }, - { id: 3, text: "Heart Disease" }, - { id: 4, text: "HyperTension" }, - { id: 5, text: "Kidney Diseases" }, - { id: 6, text: "Lung Diseases/Asthma" }, - { id: 7, text: "Cancer" }, - { id: 8, text: "OTHER" }, -]; - -export const REVIEW_AT_CHOICES: Array = [ - { id: -1, text: "No Review" }, - { id: 10, text: "10 mins" }, - { id: 15, text: "15 mins" }, - { id: 30, text: "30 mins" }, - { id: 60, text: "1 hr" }, - { id: 2 * 60, text: "2 hr" }, - { id: 3 * 60, text: "3 hr" }, - { id: 4 * 60, text: "4 hr" }, - { id: 6 * 60, text: "6 hr" }, - { id: 8 * 60, text: "8 hr" }, - { id: 12 * 60, text: "12 hr" }, - { id: 24 * 60, text: "24 hr" }, - { id: 36 * 60, text: "36 hr" }, - { id: 2 * 24 * 60, text: "2 days" }, - { id: 3 * 24 * 60, text: "3 days" }, - { id: 5 * 24 * 60, text: "5 days" }, - { id: 7 * 24 * 60, text: "7 days" }, - { id: 10 * 24 * 60, text: "10 days" }, - { id: 14 * 24 * 60, text: "2 weeks" }, - { id: 21 * 24 * 60, text: "3 weeks" }, - { id: 25 * 24 * 60, text: "25 days" }, - { id: 30 * 24 * 60, text: "1 month" }, -]; - -export const DISCHARGE_REASONS = [ - { id: 1, text: "Recovered" }, - { id: 2, text: "Referred" }, - { id: 3, text: "Expired" }, - { id: 4, text: "LAMA" }, -] as const; - export const CONSCIOUSNESS_LEVEL = [ { id: 20, value: "UNRESPONSIVE" }, { id: 15, value: "RESPONDS_TO_PAIN" }, @@ -332,14 +80,6 @@ export const CONSCIOUSNESS_LEVEL = [ }, ] as const; -export const PUPIL_REACTION_OPTIONS = [ - { id: 0, value: "UNKNOWN" }, - { id: 5, value: "BRISK" }, - { id: 10, value: "SLUGGISH" }, - { id: 15, value: "FIXED" }, - { id: 20, value: "CANNOT_BE_ASSESSED" }, -] as const; - export const LIMB_RESPONSE_OPTIONS = [ { id: 0, value: "UNKNOWN" }, { id: 5, value: "STRONG" }, @@ -373,17 +113,6 @@ export const CONSULTATION_SUGGESTION = [ { id: "DD", text: "Declare Death", editDisabled: true }, ] as const; -export type ConsultationSuggestionValue = - (typeof CONSULTATION_SUGGESTION)[number]["id"]; - -export const ADMITTED_TO = [ - { id: "1", text: "Isolation" }, - { id: "2", text: "ICU" }, - { id: "6", text: "Bed with oxygen support" }, - { id: "7", text: "Regular" }, - { id: "None", text: "No bed assigned" }, -]; - export const RESPIRATORY_SUPPORT = [ { id: "NIV", value: "NON_INVASIVE" }, { id: "IV", value: "INVASIVE" }, @@ -410,75 +139,6 @@ export const INSULIN_INTAKE_FREQUENCY_OPTIONS = [ "TD", ] as const; -export type PatientCategoryID = - | "Comfort" - | "Stable" - | "Moderate" - | "Critical" - | "ActivelyDying"; - -export const PATIENT_CATEGORIES: { - id: PatientCategoryID; - text: PatientCategory; - description: string; - twClass: string; -}[] = [ - { - id: "Comfort", // Comfort Care is discontinued - text: "Comfort Care", - twClass: "patient-comfort", - description: "End of life care", - }, - { - id: "Stable", - text: "Mild", - twClass: "patient-stable", - description: "Urgent: not life-threatening", - }, - { - id: "Moderate", - text: "Moderate", - twClass: "patient-abnormal", - description: "Emergency: could be life-threatening", - }, - { - id: "Critical", - text: "Critical", - twClass: "patient-critical", - description: "Immediate: life-threatening", - }, - { - id: "ActivelyDying", - text: "Actively Dying", - twClass: "patient-activelydying", - description: "", - }, -]; - -export const PATIENT_FILTER_CATEGORIES = PATIENT_CATEGORIES; - -export const VACCINES = [ - "CoviShield", - "Covaxin", - "Sputnik", - "Moderna", - "Pfizer", - "Janssen", - "Sinovac", -]; - -export const BLOOD_GROUPS = [ - "UNK", - "A+", - "A-", - "B+", - "B-", - "AB+", - "AB-", - "O+", - "O-", -]; - export const BLOOD_GROUP_CHOICES = [ { id: "unknown", text: "Unknown" }, { id: "A_positive", text: "A+" }, @@ -491,159 +151,6 @@ export const BLOOD_GROUP_CHOICES = [ { id: "O_negative", text: "O-" }, ]; -export const SAMPLE_TYPE_CHOICES = [ - { id: "0", text: "UNKNOWN" }, - { id: "1", text: "BA/ETA" }, - { id: "2", text: "TS/NPS/NS" }, - { id: "3", text: "Blood in EDTA" }, - { id: "4", text: "Acute Sera" }, - { id: "5", text: "Covalescent sera" }, - { id: "6", text: "Biopsy" }, - { id: "7", text: "AMR" }, - { id: "8", text: "Communicable Diseases" }, - { id: "9", text: "OTHER TYPE" }, -]; - -export const ICMR_CATEGORY = [ - "Cat 0", - "Cat 1", - "Cat 2", - "Cat 3", - "Cat 4", - "Cat 5a", - "Cat 5b", -]; - -export const TELEMEDICINE_ACTIONS = [ - { id: 10, text: "NO_ACTION", desc: "No Action" }, - { id: 20, text: "PENDING", desc: "Pending" }, - { id: 30, text: "SPECIALIST_REQUIRED", desc: "Specialist Required" }, - { id: 40, text: "PLAN_FOR_HOME_CARE", desc: "Plan for Home Care" }, - { id: 50, text: "FOLLOW_UP_NOT_REQUIRED", desc: "Follow Up Not Required" }, - { id: 60, text: "COMPLETE", desc: "Complete" }, - { id: 70, text: "REVIEW", desc: "Review" }, - { id: 80, text: "NOT_REACHABLE", desc: "Not Reachable" }, - { id: 90, text: "DISCHARGE_RECOMMENDED", desc: "Discharge Recommended" }, -]; - -export const FRONTLINE_WORKER = [ - "NOT APPLICABLE", - "HEALTHCARE WORKER", - "ELECTED REPRESENTATIVE", - "POLICE OFFICER", - "REVENUE OFFICIAL", - "TEACHER", - "FIRE FORCE", - "ANGNAWADI WORKER", - "KUDUMBASREE", - "VOLUNTEER", - "SUPERVISOR", -]; - -export const DESIGNATION_HEALTH_CARE_WORKER = [ - "AMBULANCE DRIVER", - "ASHA", - "ATTENDER", - "CLEANING STAFF", - "CSSD STAFF", - "ANEASTHESIA TECHNICHIAN", - "DIALYSIS TECHNICIAN", - "DIETICIAN", - "DOCTOR", - "FIELD STAFF", - "LAB ASSISTANT", - "LAB TECHNICIAN", - "NURSING ASSISTANT", - "OFFICE STAFF", - "PALLIATIVE NURSE", - "PHARMACIST", - "PHYSICIAN ASSISTANT", - "PHYSIOTHERAPIST", - "PSYCHOLOGIST", - "RADIOLOGY TECHNICIAN", - "SECURITY STAFF", - "SONOLOGIST", - "STAFF NURSE", - "OTHERS", -]; - -type NotificationEvent = { - id: string; - text: string; - icon: IconName; -}; - -export const NOTIFICATION_EVENTS: NotificationEvent[] = [ - { id: "MESSAGE", text: "Notice", icon: "l-comment-alt-message" }, - { - id: "PATIENT_CREATED", - text: "Patient Created", - icon: "l-user-plus", - }, - { - id: "PATIENT_UPDATED", - text: "Patient Updated", - icon: "l-edit", - }, - { - id: "PATIENT_CONSULTATION_CREATED", - text: "Patient Consultation Created", - icon: "l-heart", - }, - { - id: "PATIENT_CONSULTATION_UPDATED", - text: "Patient Consultation Updated", - icon: "l-heart-medical", - }, - { - id: "INVESTIGATION_SESSION_CREATED", - text: "Investigation Session Created", - icon: "l-search", - }, - { - id: "INVESTIGATION_UPDATED", - text: "Investigation Updated", - icon: "l-search-plus", - }, - { - id: "PATIENT_FILE_UPLOAD_CREATED", - text: "Patient File Upload Created", - icon: "l-file-medical", - }, - { - id: "CONSULTATION_FILE_UPLOAD_CREATED", - text: "Consultation File Upload Created", - icon: "l-file-upload", - }, - { - id: "PATIENT_CONSULTATION_UPDATE_CREATED", - text: "Patient Log Update Created", - icon: "l-heart", - }, - { - id: "PATIENT_CONSULTATION_UPDATE_UPDATED", - text: "Patient Log Update Updated", - icon: "l-heart-medical", - }, - { - id: "SHIFTING_UPDATED", - text: "Shifting Updated", - icon: "l-ambulance", - }, - { - id: "PATIENT_NOTE_ADDED", - text: "Patient Note Added", - icon: "l-notes", - }, -]; - -export const BREATHLESSNESS_LEVEL = [ - "NOT BREATHLESS", - "MILD", - "MODERATE", - "SEVERE", -]; - export const RESOURCE_CATEGORY_CHOICES = [ { id: "PATIENT_CARE", text: "Clinical Care and Social Support" }, { id: "COMFORT_DEVICES", text: "Comfort Devices" }, @@ -706,35 +213,6 @@ export const NURSING_CARE_PROCEDURES = [ "catheter_change", ] as const; -export const EYE_OPEN_SCALE = [ - { value: 1, text: "No Response" }, - { value: 2, text: "To Pain" }, - { value: 3, text: "To Speech" }, - { value: 4, text: "Spontaneous" }, -]; - -export const VERBAL_RESPONSE_SCALE = [ - { value: 1, text: "No Response" }, - { value: 2, text: "Incomprehensible words/Moans to pain" }, - { value: 3, text: "Inappropriate words/Cry to Pain" }, - { value: 4, text: "Confused/Irritable" }, - { value: 5, text: "Oriented to Time, Place and Person" }, -]; - -export const MOTOR_RESPONSE_SCALE = [ - { value: 1, text: "No Response" }, - { value: 2, text: "Abnormal Extension(decerebrate)" }, - { value: 3, text: "Abnormal Flexion(decorticate)" }, - { value: 4, text: "Flexion/Withdrawal from pain" }, - { value: 5, text: "Moves to localized pain" }, - { value: 6, text: "Obeying commands/Normal acrivity" }, -]; - -export const RHYTHM_CHOICES = [ - { id: 5, text: "REGULAR", desc: "Regular" }, - { id: 10, text: "IRREGULAR", desc: "Irregular" }, -] as const; - export const BOWEL_ISSUE_CHOICES = [ "NO_DIFFICULTY", "CONSTIPATION", @@ -794,130 +272,6 @@ export const APPETITE_CHOICES = [ "CANNOT_BE_ASSESSED", ] as const; -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 type CameraPTZ = { - icon?: IconName; - label: string; - action: string; - loadingLabel?: string; - shortcutKey: string[]; - value?: number; -}; - -export const CAMERA_STATES = { - IDLE: "idle", - MOVING: { - GENERIC: "Moving", - UP: "Moving Up", - DOWN: "Moving Down", - LEFT: "Moving Left", - RIGHT: "Moving Right", - }, - ZOOMING: { - IN: "Zooming In", - OUT: "Zooming Out", - }, - PRECISION: "Setting Precision", - UPDATING_PRESET: "Updating Preset", -}; - -export const getCameraPTZ: (precision: number) => CameraPTZ[] = (precision) => [ - { - icon: "l-angle-up", - label: "Move Up", - action: "up", - loadingLabel: CAMERA_STATES.MOVING.UP, - shortcutKey: ["Control", "Shift", "ArrowUp"], - }, - { - icon: "l-angle-down", - label: "Move Down", - action: "down", - loadingLabel: CAMERA_STATES.MOVING.DOWN, - shortcutKey: ["Control", "Shift", "ArrowDown"], - }, - { - icon: "l-angle-left", - label: "Move Left", - action: "left", - loadingLabel: CAMERA_STATES.MOVING.LEFT, - shortcutKey: ["Control", "Shift", "ArrowLeft"], - }, - { - icon: "l-angle-right", - label: "Move Right", - action: "right", - loadingLabel: CAMERA_STATES.MOVING.RIGHT, - shortcutKey: ["Control", "Shift", "ArrowRight"], - }, - { - value: precision, - label: "Precision", - action: "precision", - loadingLabel: CAMERA_STATES.PRECISION, - shortcutKey: ["Shift", "P"], - }, - { - icon: "l-search-plus", - label: "Zoom In", - action: "zoomIn", - loadingLabel: CAMERA_STATES.ZOOMING.IN, - shortcutKey: ["Shift", "I"], - }, - { - icon: "l-search-minus", - label: "Zoom Out", - action: "zoomOut", - loadingLabel: CAMERA_STATES.ZOOMING.OUT, - shortcutKey: ["Shift", "O"], - }, - { - icon: "l-save", - label: "Update Preset", - action: "updatePreset", - loadingLabel: CAMERA_STATES.UPDATING_PRESET, - shortcutKey: ["Shift", "S"], - }, - { - icon: "l-redo", - label: "Reset", - action: "reset", - shortcutKey: ["Shift", "R"], - }, - { - icon: "l-expand-arrows-alt", - label: "Full Screen", - action: "fullScreen", - shortcutKey: ["F"], - }, -]; - -// FEATURE_CHOICES = [ -// (1, "CT Scan Facility"), -// (2, "Maternity Care"), -// (3, "X-Ray facility"), -// (4, "Neonatal care"), -// (5, "Operation theater"), -// (6, "Blood Bank"), -// (7, "Emergency Services"), -// (8, "Inpatient Services"), -// (9, "Outpatient Services"), -// (10, "Intensive Care Units"), -// (11, "Pharmacy"), -// (12, "Rehabilitation Services"), -// (13, "Home Care Services"), -// (14, "Psychosocial Support"), -// (15, "Respite Care"), -// (16, "Daycare Programs"), -// ] - -// in future, if you find Unicon equivalents of all these icons, please replace them. Only use the same iconset throughout. export const FACILITY_FEATURE_TYPES: { id: number; name: string; @@ -1022,193 +376,6 @@ export const FACILITY_FEATURE_TYPES: { }, ]; -export const WAVEFORM_VIEWABLE_LENGTH = 400; - -//blacklisted paths will not scroll to top -export const BLACKLISTED_PATHS: RegExp[] = [ - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/feed+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/summary+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/medicines+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/files+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/investigations+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/abg+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/nursing+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/neurological_monitoring+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/ventilator+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/nutrition+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/pressure_sore+/i, - /\/facility\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/patient\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/consultation\/([A-Za-z0-9]+(-[A-Za-z0-9]+)+)\/dialysis+/i, -]; - -export const AssetImportSchema: SchemaType = { - Name: { prop: "name", type: "string" }, - Type: { - prop: "asset_type", - type: "string", - oneOf: ["INTERNAL", "EXTERNAL"], - required: true, - }, - Class: { - prop: "asset_class", - type: "string", - oneOf: ["HL7MONITOR", "ONVIF", "VENTILATOR", ""], - }, - Description: { prop: "description", type: "string" }, - "Working Status": { - prop: "is_working", - type: "boolean", - parse: (status: string) => { - if (status === "WORKING") { - return true; - } else if (status === "NOT WORKING") { - return false; - } else { - throw new Error("Invalid Working Status: " + status); - } - }, - required: true, - }, - "Not Working Reason": { prop: "not_working_reason", type: "string" }, - "Serial Number": { prop: "serial_number", type: "string" }, - "QR Code ID": { prop: "qr_code_id", type: "string" }, - Manufacturer: { prop: "manufacturer", type: "string" }, - "Vendor Name": { prop: "vendor_name", type: "string" }, - "Support Name": { prop: "support_name", type: "string" }, - "Support Email": { - prop: "support_email", - type: "string", - parse: (email: string) => { - if (!email) return null; - const isValid = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/.test(email); - - if (!isValid) { - throw new Error("Invalid Support Email: " + email); - } - - return email; - }, - }, - "Support Phone Number": { - prop: "support_phone", - type: "string", - parse: (phone: number | string) => { - phone = String(phone); - if (phone.length === 10 && !phone.startsWith("1800")) { - phone = "+91" + phone; - } - if (phone.startsWith("91") && phone.length === 12) { - phone = "+" + phone; - } - if (phone.startsWith("+911800")) { - phone = "1800" + phone.slice(6); - } - if ( - PhoneNumberValidator(["mobile", "landline", "support"])(phone) !== - undefined - ) { - throw new Error("Invalid Support Phone Number: " + phone); - } - - return phone ? phone : undefined; - }, - required: true, - }, - "Warranty End Date": { - prop: "warranty_amc_end_of_validity", - type: "string", - parse: (date: string) => { - if (!date) return null; - //handles both "YYYY-MM-DD" and long date format eg : Wed Oct 14 2020 05:30:00 GMT+0530 (India Standard Time) - if (isNaN(Date.parse(date))) { - const parts = date.split("-"); - if (parts.length !== 3) { - throw new Error("Invalid Date Format: " + date); - } - const reformattedDateStr = `${parts[2]}-${parts[1]}-${parts[0]}`; - const parsed = new Date(reformattedDateStr); - if (String(parsed) === "Invalid Date") { - throw new Error("Invalid Date: " + date); - } - return dateQueryString(parsed); - } else { - const parsed = new Date(date); - if (String(parsed) === "Invalid Date") { - throw new Error("Invalid Date: " + date); - } - return dateQueryString(parsed); - } - }, - }, - "Last Service Date": { - prop: "last_serviced_on", - type: "string", - parse: (date: string) => { - if (!date) return null; - if (isNaN(Date.parse(date))) { - const parts = date.split("-"); - if (parts.length !== 3) { - throw new Error("Invalid Date Format: " + date); - } - const reformattedDateStr = `${parts[2]}-${parts[1]}-${parts[0]}`; - const parsed = new Date(reformattedDateStr); - if (String(parsed) === "Invalid Date") { - throw new Error("Invalid Date: " + date); - } - return dateQueryString(parsed); - } else { - const parsed = new Date(date); - if (String(parsed) === "Invalid Date") { - throw new Error("Invalid Date: " + date); - } - return dateQueryString(parsed); - } - }, - }, - Notes: { prop: "note", type: "string" }, - "Config - IP Address": { - parent: "meta", - prop: "local_ip_address", - type: "string", - parse: (ip: string) => { - if (!ip) return null; - const isValid = - /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test( - ip, - ); - - if (!isValid) { - throw new Error("Invalid Config IP Address: " + ip); - } - - return ip; - }, - }, - "Config: Camera Access Key": { - parent: "meta", - prop: "camera_access_key", - type: "string", - }, -}; - -export const USER_TYPES_MAP = { - Pharmacist: "Pharmacist", - Volunteer: "Volunteer", - StaffReadOnly: "Staff", - Staff: "Staff", - Doctor: "Doctor", - Nurse: "Nurse", - NurseReadOnly: "Nurse", - WardAdmin: "Ward Admin", - LocalBodyAdmin: "Local Body Admin", - DistrictLabAdmin: "District Lab Admin", - DistrictReadOnlyAdmin: "District Admin", - DistrictAdmin: "District Admin", - StateLabAdmin: "State Lab Admin", - StateReadOnlyAdmin: "State Admin", - StateAdmin: "State Admin", - RemoteSpecialist: "Remote Specialist", -} as const; - export const AREACODES: Record = { CA: [ "403", @@ -1332,21 +499,6 @@ export const IN_LANDLINE_AREA_CODES = [ "4822", ]; -export const CONSENT_TYPE_CHOICES = [ - { id: 1, text: "Consent for admission" }, - { id: 2, text: "Patient Code Status" }, - { id: 3, text: "Consent for procedure" }, - { id: 4, text: "High risk consent" }, - { id: 5, text: "Others" }, -]; - -export const CONSENT_PATIENT_CODE_STATUS_CHOICES = [ - { id: 1, text: "Do Not Hospitalise (DNH)" }, - { id: 2, text: "Do Not Resuscitate (DNR)" }, - { id: 3, text: "Comfort Care Only" }, - { id: 4, text: "Active treatment" }, -]; - export const SOCIOECONOMIC_STATUS_CHOICES = [ "MIDDLE_CLASS", "POOR", diff --git a/src/common/utils.tsx b/src/common/utils.tsx index 7089851a018..a2f4a681037 100644 --- a/src/common/utils.tsx +++ b/src/common/utils.tsx @@ -1,53 +1,3 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { useEffect } from "react"; - -import { OptionsType } from "@/common/constants"; - -import { humanizeStrings } from "@/Utils/utils"; - -export interface statusType { - aborted?: boolean; -} - -type AbortableFunction = (status: statusType) => any; - -export const useAbortableEffect = ( - effect: AbortableFunction, - dependencies: Array, -) => { - const status: statusType = {}; // mutable status object - useEffect(() => { - status.aborted = false; - // pass the mutable object to the effect callback - // store the returned value for cleanup - const cleanUpFn = effect(status); - return () => { - // mutate the object to signal the consumer - // this effect is cleaning up - status.aborted = true; - if (typeof cleanUpFn === "function") { - // run the cleanup function - cleanUpFn(); - } - }; - }, [...dependencies]); -}; - -export const parseOptionId: ( - options: readonly OptionsType[], - id: string | string[], -) => string = (options, id) => { - return humanizeStrings( - options - .filter((option) => { - return id instanceof Array - ? id.map((i) => String(i)).includes(String(option.id)) - : String(option.id) === String(id); - }) - .map((option) => option.text), - ); -}; - export const deepEqual = (x: any, y: any): boolean => { if (x === y) return true; diff --git a/src/common/validation.tsx b/src/common/validation.tsx index f091303a452..1c6343f6069 100644 --- a/src/common/validation.tsx +++ b/src/common/validation.tsx @@ -14,38 +14,10 @@ export const validateLongitude = (longitude: string) => { return valueIsBetween(Number(longitude), -180, 180); }; -export const validateEmailAddress = (email: string) => { - const pattern = - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - return pattern.test(email); -}; - -export const getArrayValueByKey = ( - arr: Array, - attr: string, - value: string | number, -) => { - for (let i = 0; i < arr.length; i++) { - if (arr[i][attr] === value) { - return i; - } - } - return -1; -}; - -export const getRandomNumbers = (min: number, max: number) => { - return Math.floor(Math.random() * max) + min; -}; - export const validateName = (name: string) => { return name.length >= 3; }; -export const validateUsername = (username: string) => { - const pattern = /^(?!.*[._-]{2})[a-z0-9](?:[a-z0-9._-]{2,14}[a-z0-9])$/s; - return pattern.test(username); -}; - export const validatePassword = (password: string) => { const pattern = /(?=(.*[0-9]))((?=.*[A-Za-z0-9])(?=.*[A-Z])(?=.*[a-z]))^.{8,}$/; @@ -56,16 +28,3 @@ export const validatePincode = (pincode: string) => { const pattern = /^[1-9][0-9]{5}$/; return pattern.test(pincode); }; - -export const validateNumber = (number: string) => { - const pattern = /^[0-9]+$/; - return pattern.test(number); -}; - -export const checkIfValidIP = (str: string) => { - // Regular expression to check if string is a IP address - const regexExp = - /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/gi; - - return regexExp.test(str); -}; diff --git a/src/components/Common/Export.tsx b/src/components/Common/Export.tsx index 90f31eba96d..6f9c6bdbb7d 100644 --- a/src/components/Common/Export.tsx +++ b/src/components/Common/Export.tsx @@ -6,32 +6,11 @@ import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; -import DropdownMenu, { - DropdownItem, - DropdownItemProps, -} from "@/components/Common/Menu"; - import useExport from "@/hooks/useExport"; import request from "@/Utils/request/request"; import { ApiRoute } from "@/Utils/request/types"; -interface ExportItem { - options?: DropdownItemProps; - type?: "csv" | "json"; - filePrefix?: string; - label: string; - parse?: (data: string) => string; - action?: Parameters["exportFile"]>[0]; - route?: ApiRoute; -} - -interface ExportMenuProps { - disabled?: boolean | undefined; - label?: string; - exportItems: ExportItem[]; -} - interface ExportButtonProps { disabled?: boolean | undefined; tooltip?: string | undefined; @@ -45,74 +24,6 @@ interface ExportButtonProps { variant?: "primary_gradient" | "secondary"; } -export const ExportMenu = ({ - label = "Export", - disabled, - exportItems, -}: ExportMenuProps) => { - const { isExporting, exportFile } = useExport(); - const { t } = useTranslation(); - - if (exportItems.length === 1) { - const item = exportItems[0]; - - return ( - - ); - } - - return ( -
- } - className="tooltip border-primary-500 bg-white text-primary-500 hover:bg-primary-100 enabled:border" - > - {exportItems.map((item) => ( - { - let action = item.action; - if (item.route) { - action = async () => { - const { data } = await request(item.route!); - return data ?? null; - }; - } - if (action) { - exportFile(action, item.filePrefix, item.type, item.parse); - } - }} - {...item.options} - > - {item.label} - - ))} - -
- ); -}; - export const ExportButton = ({ tooltipClassName = "tooltip-bottom -translate-x-7", variant, @@ -158,5 +69,3 @@ export const ExportButton = ({ ); }; - -export default ExportMenu; diff --git a/src/components/Common/Menu.tsx b/src/components/Common/Menu.tsx deleted file mode 100644 index 6568e5b143c..00000000000 --- a/src/components/Common/Menu.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import { Menu, MenuButton, MenuItem, MenuItems } from "@headlessui/react"; -import { DetailedHTMLProps, HTMLAttributes, ReactNode } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { buttonVariants } from "@/components/ui/button"; - -import { useIsAuthorized } from "@/hooks/useIsAuthorized"; - -import { Anyone, AuthorizedElementProps } from "@/Utils/AuthorizeFor"; -import { classNames } from "@/Utils/utils"; - -interface DropdownMenuProps { - id?: string; - title: string; - icon?: JSX.Element | undefined; - children: ReactNode | ReactNode[]; - disabled?: boolean | undefined; - className?: string | undefined; - itemClassName?: string | undefined; - containerClassName?: string | undefined; -} - -/** - * @deprecated This component will be replaced with ShadCN's dropdown. - */ - -export default function DropdownMenu({ ...props }: DropdownMenuProps) { - return ( -
- - -
- {props.icon} - {props.title || "Dropdown"} -
- -
- - - <>{props.children} - -
-
- ); -} - -type RawDivProps = DetailedHTMLProps< - HTMLAttributes, - HTMLDivElement ->; - -export type DropdownItemProps = RawDivProps & - AuthorizedElementProps & { - icon?: ReactNode | undefined; - disabled?: boolean | undefined; - }; - -export function DropdownItem({ - authorizeFor = Anyone, - className, - icon, - children, - ...props -}: DropdownItemProps) { - const isAuthorized = useIsAuthorized(authorizeFor); - - return ( - -
- {icon} - {children} -
-
- ); -} diff --git a/src/components/Common/Pagination.tsx b/src/components/Common/Pagination.tsx index 6db267ed9c7..4f160e4b434 100644 --- a/src/components/Common/Pagination.tsx +++ b/src/components/Common/Pagination.tsx @@ -1,11 +1,9 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { Button } from "@/components/ui/button"; -import { statusType, useAbortableEffect } from "@/common/utils"; - interface PaginationProps { data: { totalCount: number }; onChange: (page: number, rowsPerPage: number) => void; @@ -23,19 +21,14 @@ const Pagination = ({ const [rowsPerPage, setRowsPerPage] = useState(3); const [currentPage, setCurrentPage] = useState(1); - useAbortableEffect( - (status: statusType) => { - if (!status.aborted) { - if (defaultPerPage) { - setRowsPerPage(defaultPerPage); - } - if (cPage) { - setCurrentPage(parseInt(`${cPage}`)); - } - } - }, - [defaultPerPage, cPage], - ); + useEffect(() => { + if (defaultPerPage) { + setRowsPerPage(defaultPerPage); + } + if (cPage) { + setCurrentPage(parseInt(`${cPage}`)); + } + }, [defaultPerPage, cPage]); const getPageNumbers = () => { const totalPage = Math.ceil(data.totalCount / rowsPerPage); diff --git a/src/components/Common/PatientCategoryBadge.tsx b/src/components/Common/PatientCategoryBadge.tsx deleted file mode 100644 index 466a46aa9a9..00000000000 --- a/src/components/Common/PatientCategoryBadge.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { PatientCategory } from "@/components/Facility/models"; - -import { PATIENT_CATEGORIES } from "@/common/constants"; - -const PatientCategoryBadge = ({ category }: { category?: PatientCategory }) => { - const categoryClass = category - ? PATIENT_CATEGORIES.find((c) => c.text === category)?.twClass - : "patient-unknown"; - - return ( - - {category} - - ); -}; - -export default PatientCategoryBadge; diff --git a/src/components/Common/Script.tsx b/src/components/Common/Script.tsx deleted file mode 100644 index fcfd7838815..00000000000 --- a/src/components/Common/Script.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useEffect } from "react"; - -interface Props { - id?: string; - defer?: boolean; - src: string; - [key: string]: any; -} - -/** - * Dynamically load a script into the page. - * To refresh the script, change the `key` prop. - */ -export default function Script({ id, defer = false, src, ...attrs }: Props) { - useEffect(() => { - const script = document.createElement("script"); - - if (id) script.id = id; - script.src = src; - script.async = true; - script.defer = defer; - - Object.entries(attrs).forEach((e) => script.setAttribute(...e)); - - document.body.appendChild(script); - - return () => { - document.body.removeChild(script); - }; - }, [id, defer, src]); - - return null; -} diff --git a/src/components/Common/Steps.tsx b/src/components/Common/Steps.tsx deleted file mode 100644 index bf1cc52d5b9..00000000000 --- a/src/components/Common/Steps.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import CareIcon from "@/CAREUI/icons/CareIcon"; - -export interface Step { - id: number; - name: string; - onClick: () => void; - status: "complete" | "current" | "upcoming"; - disabled?: boolean; -} - -export default function Steps(props: { steps: Step[] }) { - return ( - - ); -} diff --git a/src/components/Common/Switch.tsx b/src/components/Common/Switch.tsx deleted file mode 100644 index 44d09146ef5..00000000000 --- a/src/components/Common/Switch.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import { - FieldErrorText, - FieldLabel, -} from "@/components/Form/FormFields/FormField"; - -type SwitchProps = { - name?: string; - className?: string; - label?: string; - value?: T; - options: T[]; - optionLabel?: (option: T) => string; - optionClassName?: (option: T) => string | false; - required?: boolean; - onChange: (option: T) => void; - error?: string; -}; - -export default function SwitchV2(props: SwitchProps) { - return ( -
- - {props.label} - -
    - {props.options.map((option, index) => { - const selected = option === props.value; - const additionalClassNames = selected - ? (props.optionClassName && props.optionClassName(option)) || - "bg-primary-500 hover:bg-primary-600 text-white border-primary-500 focus:ring-primary-500 focus:border-primary-500" - : "bg-secondary-50 hover:bg-secondary-200 border-secondary-400 focus:ring-primary-500 focus:border-primary-500"; - - return ( -
  • props.onChange(option)} - > - - {(props.optionLabel && props.optionLabel(option)) || - String(option)} - -
  • - ); - })} -
- -
- ); -} diff --git a/src/components/Common/UserAutocompleteFormField.tsx b/src/components/Common/UserAutocompleteFormField.tsx index 2cc34522fad..90addafb702 100644 --- a/src/components/Common/UserAutocompleteFormField.tsx +++ b/src/components/Common/UserAutocompleteFormField.tsx @@ -6,10 +6,9 @@ import { FormFieldBaseProps, useFormFieldPropsResolver, } from "@/components/Form/FormFields/Utils"; +import { UserType } from "@/components/Users/UserFormValidations"; import { UserBareMinimum } from "@/components/Users/models"; -import { UserRole } from "@/common/constants"; - import routes from "@/Utils/request/api"; import useTanStackQueryInstead from "@/Utils/request/useQuery"; import { @@ -23,15 +22,10 @@ import { Avatar } from "./Avatar"; type BaseProps = FormFieldBaseProps & { placeholder?: string; - userType?: UserRole; + userType?: UserType; noResultsError?: string; }; -type LinkedFacilitySearchProps = BaseProps & { - facilityId: string; - homeFacility?: undefined; -}; - type UserSearchProps = BaseProps & { facilityId?: undefined; homeFacility?: string; @@ -107,63 +101,6 @@ export default function UserAutocomplete(props: UserSearchProps) { ); } -export const LinkedFacilityUsers = (props: LinkedFacilitySearchProps) => { - const field = useFormFieldPropsResolver(props); - - const [query, setQuery] = useState(""); - - const { data, loading } = useTanStackQueryInstead(routes.getFacilityUsers, { - pathParams: { facility_id: props.facilityId }, - query: { - user_type: props.userType, - search_text: query, - limit: 50, - offset: 0, - }, - }); - - const noResultError = - (!query && - !loading && - field.required && - !data?.results?.length && - props.noResultsError) || - undefined; - - useEffect(() => { - if (noResultError) { - field.handleChange(undefined as unknown as UserBareMinimum); - } - }, [noResultError]); - - return ( - - obj.username, - )} - optionLabel={formatName} - optionIcon={userOnlineDot} - optionDescription={(option) => - `${option.user_type} - ${option.username}` - } - optionValue={(option) => option} - onQuery={setQuery} - isLoading={loading} - /> - - ); -}; - const userOnlineDot = (user: UserBareMinimum) => (
( -
- -
-
- {value} -
-
-
-
-); - -export default UserDetailComponent; diff --git a/src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx b/src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx deleted file mode 100644 index 6183f19368d..00000000000 --- a/src/components/Facility/Consultations/components/BinaryChronologicalChart.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { formatDateTime } from "@/Utils/utils"; - -export default function BinaryChronologicalChart(props: { - data: { - value: boolean | undefined; - timestamp: string; - notes?: string; - }[]; - trueName?: string; - falseName?: string; - title: string; -}) { - const { data, title, trueName = "true", falseName = "false" } = props; - - return ( -
-

{title}

-
-
    - {data.map((entry, i) => ( -
  • -
    -
    -
  • - ))} -
-
-
- ); -} diff --git a/src/components/Facility/Consultations/components/LinePlot.tsx b/src/components/Facility/Consultations/components/LinePlot.tsx deleted file mode 100644 index e9f1adf731f..00000000000 --- a/src/components/Facility/Consultations/components/LinePlot.tsx +++ /dev/null @@ -1,259 +0,0 @@ -import { Suspense, lazy } from "react"; - -import CircularProgress from "@/components/Common/CircularProgress"; - -import { properRoundOf } from "@/Utils/utils"; - -const ReactEcharts = lazy( - () => import("@/components/Facility/Consultations/components/ReactEcharts"), -); - -export const LinePlot = (props: any) => { - const { - title, - name, - low = null, - high = null, - defaultSpace, - verticalMarkerData = null, - } = props; - let { xData, yData } = props; - const yDatacount = yData.filter( - (item: number | null): item is number => - item !== null && !Number.isNaN(item), - ).length; - if (yDatacount === 0) { - yData = []; - xData = []; - } - - let generalOptions: any = { - grid: { - top: "40px", - left: "20px", - right: "30px", - containLabel: true, - }, - title: { - text: `${title} [ {0|${ - yData[yData.length - 1] ? properRoundOf(yData[yData.length - 1]) : "NA" - }} ]`, - textStyle: { - fontSize: 14, - rich: { - 0: { - fontSize: 14, - fontWeight: "bold", - padding: [0, 5], - color: "#5470C6", - }, - }, - }, - }, - legend: { - data: [name], - type: "scroll", - bottom: "3%", - }, - tooltip: { - trigger: "axis", - confine: true, - }, - toolbox: { - show: true, - orient: "vertical", - top: "9%", - feature: { - dataZoom: { - yAxisIndex: "none", - }, - magicType: { type: ["line", "bar"] }, - saveAsImage: {}, - }, - }, - xAxis: { - type: "category", - boundaryGap: false, - data: xData, - axisLabel: { - width: 60, - overflow: "break", - align: "center", - }, - }, - yAxis: { - type: "value", - }, - series: [ - { - name: name, - type: "line", - stack: name, - data: yData, - areaStyle: { - color: { - type: "linear", - x: 0, - y: 0, - x2: 0, - y2: 1, - opacity: 0.5, - colorStops: [ - { - offset: 0, - color: "blue", - }, - { - offset: 1, - color: "white", - }, - ], - }, - }, - connectNulls: true, - }, - ], - }; - - if (verticalMarkerData && yDatacount > 0) { - let series = generalOptions.series[0]; - series = { - ...series, - markLine: { - silent: true, - data: verticalMarkerData, - symbol: "none", - lineStyle: { - color: "#000000", - }, - }, - }; - generalOptions = { - ...generalOptions, - series, - }; - } - - if (props.type && props.type === "WAVEFORM") { - generalOptions = { - ...generalOptions, - title: { - text: "", - }, - grid: { - show: false, - borderColor: "transparent", - left: "40px", - right: "20px", - }, - animation: false, - xAxis: { - ...generalOptions.xAxis, - show: false, - }, - yAxis: { - ...generalOptions.yAxis, - show: true, - min: props.yStart, - max: props.yEnd, - splitLine: { - lineStyle: { - color: "#4E4E4E", - }, - }, - }, - toolbox: { - ...generalOptions.toolbox, - show: false, - }, - legend: { - show: false, - }, - series: [ - { - ...generalOptions.series[0], - showSymbol: false, - lineStyle: { color: props.color }, - areaStyle: { - ...generalOptions.series[0].areaStyle, - color: { - ...generalOptions.series[0].areaStyle.color, - colorStops: [ - { - offset: 0, - color: "transparent", - }, - { - offset: 1, - color: "transparent", - }, - ], - }, - }, - connectNulls: false, - }, - ], - }; - } - - if (props.type === "WAVEFORM" && defaultSpace != true) { - generalOptions = { - ...generalOptions, - grid: { - ...generalOptions.grid, - top: "20px", - bottom: "20px", - }, - }; - } - - const visualMap: any = { - visualMap: { - type: "piecewise", - show: false, - dimension: 1, - pieces: [ - { - gt: high, - color: "red", - }, - { - lte: high, - gte: low, - color: "blue", - }, - { - lt: low, - color: "red", - }, - ], - }, - }; - - if (high && low) { - generalOptions = { ...generalOptions, ...visualMap }; - } - - return ( - <> - {yData.map((value: any, idx: any) => ( - - {value ? properRoundOf(value) : "NA"} - - ))} - - -
- } - > - - - - ); -}; diff --git a/src/components/Facility/Consultations/components/ReactEcharts.tsx b/src/components/Facility/Consultations/components/ReactEcharts.tsx deleted file mode 100644 index 4215b34fa4d..00000000000 --- a/src/components/Facility/Consultations/components/ReactEcharts.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { EChartsReactProps } from "echarts-for-react"; -import ReactEchartsCore from "echarts-for-react/lib/core"; -import { BarChart, LineChart } from "echarts/charts"; -import { - DataZoomComponent, - GridComponent, - LegendComponent, - MarkLineComponent, - TitleComponent, - ToolboxComponent, - TooltipComponent, - VisualMapComponent, - VisualMapPiecewiseComponent, -} from "echarts/components"; -import * as echarts from "echarts/core"; -import { CanvasRenderer } from "echarts/renderers"; -import { memo } from "react"; - -echarts.use([ - BarChart, - LineChart, - CanvasRenderer, - DataZoomComponent, - GridComponent, - LegendComponent, - TitleComponent, - ToolboxComponent, - TooltipComponent, - VisualMapComponent, - VisualMapPiecewiseComponent, - MarkLineComponent, -]); - -interface ReactEchartsProps extends EChartsReactProps { - echarts?: typeof echarts; -} - -function ReactEcharts(props: ReactEchartsProps) { - const enhancedProps = { - ...props, - echarts: props.echarts || echarts, - }; - return ; -} - -export default memo(ReactEcharts); diff --git a/src/components/Facility/Consultations/components/StackedLinePlot.tsx b/src/components/Facility/Consultations/components/StackedLinePlot.tsx deleted file mode 100644 index 491320736ee..00000000000 --- a/src/components/Facility/Consultations/components/StackedLinePlot.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { Suspense, lazy } from "react"; - -import CircularProgress from "@/components/Common/CircularProgress"; - -const ReactEcharts = lazy( - () => import("@/components/Facility/Consultations/components/ReactEcharts"), -); - -interface DataPoint { - name: string; - data: number[]; -} - -interface StackedLinePlotProps { - title: string; - xData: string[]; - yData: DataPoint[]; -} -const COLORS = ["#B13F3C", "#2F8B35", "#44327A", "#B19D3C"]; - -export const StackedLinePlot = (props: StackedLinePlotProps) => { - const { title, xData, yData } = props; - - const series = yData.map((x: any) => ({ - name: x.name, - type: "line", - stack: x.name, - data: x.data.map((d: any) => Math.round(d * 100) / 100), - connectNulls: true, - })); - - const generalOptions = { - grid: { - left: "20px", - right: "30px", - containLabel: true, - }, - color: COLORS, - title: { - text: - title + - " [ " + - yData - .map( - (x: any, i: number) => - `{${i}|${x.data[x.data.length - 1]?.toFixed(2) ?? "NA"}}`, - ) - .join(" | ") + - " ] ", - textStyle: { - fontSize: 14, - rich: Object.assign( - {}, - COLORS.map((x: any) => ({ - fontSize: 14, - fontWeight: "bold", - padding: [0, 5], - color: x, - })), - ), - }, - }, - - legend: { - data: yData.map((x: any) => x.name), - type: "scroll", - bottom: "3%", - }, - tooltip: { - trigger: "axis", - confine: true, - }, - toolbox: { - show: true, - orient: "vertical", - top: "9%", - feature: { - dataZoom: { - yAxisIndex: "none", - }, - magicType: { type: ["line", "bar"] }, - saveAsImage: {}, - }, - }, - xAxis: { - type: "category", - boundaryGap: false, - data: xData, - axisLabel: { - width: 60, - overflow: "break", - align: "center", - }, - }, - yAxis: { - type: "value", - }, - series: series, - }; - return ( - - -
- } - > - - - ); -}; diff --git a/src/components/Facility/FacilityHome.tsx b/src/components/Facility/FacilityHome.tsx index 4ec23fe5a65..1dbaf401e40 100644 --- a/src/components/Facility/FacilityHome.tsx +++ b/src/components/Facility/FacilityHome.tsx @@ -1,6 +1,6 @@ import careConfig from "@careConfig"; import { useQuery } from "@tanstack/react-query"; -import { Hospital, MapPin, MoreVertical, Settings, Trash2 } from "lucide-react"; +import { Hospital, MapPin, MoreVertical, Settings } from "lucide-react"; import { navigate } from "raviger"; import { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -23,8 +23,6 @@ import ConfirmDialog from "@/components/Common/ConfirmDialog"; import ContactLink from "@/components/Common/ContactLink"; import Loading from "@/components/Common/Loading"; -import useAuthUser from "@/hooks/useAuthUser"; - import { FACILITY_FEATURE_TYPES } from "@/common/constants"; import routes from "@/Utils/request/api"; @@ -40,15 +38,6 @@ import type { } from "@/types/organization/organization"; import { getOrgLabel } from "@/types/organization/organization"; -import type { UserModel } from "../Users/models"; - -export function canUserRegisterPatient( - authUser: UserModel, - facilityId: string, -) { - return authUser.home_facility_object?.id === facilityId; -} - type Props = { facilityId: string; }; @@ -100,7 +89,6 @@ export const FacilityHome = ({ facilityId }: Props) => { const { t } = useTranslation(); const [openDeleteDialog, setOpenDeleteDialog] = useState(false); const [editCoverImage, setEditCoverImage] = useState(false); - const authUser = useAuthUser(); const { data: facilityData, @@ -176,9 +164,6 @@ export const FacilityHome = ({ facilityId }: Props) => { } const hasPermissionToEditCoverImage = true; - const hasPermissionToDeleteFacility = - authUser.user_type === "DistrictAdmin" || - authUser.user_type === "StateAdmin"; return (
@@ -263,7 +248,8 @@ export const FacilityHome = ({ facilityId }: Props) => { {t("update_facility")} - {hasPermissionToDeleteFacility && ( + {/* TODO: get permissions from backend */} + {/* {hasPermissionToDeleteFacility && ( setOpenDeleteDialog(true)} @@ -271,7 +257,7 @@ export const FacilityHome = ({ facilityId }: Props) => { {t("delete_facility")} - )} + )} */}
diff --git a/src/components/Facility/models.tsx b/src/components/Facility/models.tsx index 2ebbfc7161a..aba560973ea 100644 --- a/src/components/Facility/models.tsx +++ b/src/components/Facility/models.tsx @@ -1,14 +1,6 @@ -import { FileUploadModel } from "@/components/Patient/models"; import { UserBareMinimum } from "@/components/Users/models"; -import { - CONSENT_PATIENT_CODE_STATUS_CHOICES, - CONSENT_TYPE_CHOICES, - UserRole, -} from "@/common/constants"; - import { FeatureFlag } from "@/Utils/featureFlags"; -import { PatientModel } from "@/types/emr/patient"; export interface FacilityModel { id?: string; @@ -34,12 +26,6 @@ export interface FacilityModel { is_public?: boolean; } -export interface OptionsType { - id: number; - text: string; - disabled?: boolean; -} - export type PatientCategory = | "Comfort Care" // Discontinued | "Mild" @@ -47,176 +33,12 @@ export type PatientCategory = | "Critical" | "Actively Dying"; -export interface PatientConsentModel { - id: string; - type: (typeof CONSENT_TYPE_CHOICES)[number]["id"]; - patient_code_status: - | (typeof CONSENT_PATIENT_CODE_STATUS_CHOICES)[number]["id"] - | null; - files: FileUploadModel[] | null; - archived: boolean; - archived_by?: UserBareMinimum; - archived_date: string; - created_date: string; - created_by: UserBareMinimum; -} - -export interface DupPatientModel { - id: string; - gender: string; - phone_number: string; - patient_id: string; - name: string; - date_of_birth: string; - year_of_birth: number; - is_expired: boolean; -} - -export interface InventoryItemsModel { - // count?: number; - id?: number; - name?: string; - default_unit?: { - id: number; - name: string; - }; - allowed_units?: [ - { - id: number; - name: string; - }, - ]; -} - -export interface FacilityType { - id: number; - name: string; -} - -export interface BaseUserModel { - id: number; - first_name: string; - username: string; - email: string; - last_name: string; - user_type: string; - last_login: string; -} - -export interface PatientNotesEditModel { - id: string; - edited_by: BaseUserModel; - edited_date: string; - note: string; -} - -export interface PaitentNotesReplyModel { - id: string; - note: string; - user_type?: UserRole | "RemoteSpecialist"; - created_by_object: BaseUserModel; - created_date: string; -} - -export type IFacilityNotificationRequest = { - facility: string; - message: string; -}; - -export type IFacilityNotificationResponse = { - [key: string]: string; -}; - export type IUserFacilityRequest = { facility: string; }; export type FacilityRequest = Omit; -export type InventorySummaryResponse = { - id: string; - item_object: { - id: number; - default_unit: { - id: number; - name: string; - }; - allowed_units: { - id: number; - name: string; - }[]; - tags: { - id: number; - name: string; - }[]; - name: string; - description: string; - min_quantity: number; - }; - unit_object: { - id: number; - name: string; - }; - created_date: string; - quantity: number; - is_low: boolean; - item: number; -}; - -export type MinimumQuantityItemResponse = { - id: string; - item_object: InventoryItemsModel; - created_date: string; - min_quantity: number; - item: number; -}; - -export type InventoryLogResponse = InventorySummaryResponse & { - external_id: string; - current_stock: number; - quantity_in_default_unit: number; - is_incoming: boolean; - probable_accident: boolean; - unit: number; - created_by: number; -}; - -export type PatientTransferRequest = { - phone_number: string; - year_of_birth: string; -}; - -export interface ResourceModel { - approving_facility: string | null; - approving_facility_object: FacilityModel | null; - assigned_facility: string | null; - assigned_facility_object: FacilityModel | null; - assigned_quantity: number; - assigned_to: string | null; - category: string; - created_by: number; - emergency: boolean; - id: string; - is_assigned_to_user: boolean; - last_edited_by: number; - modified_date: string; - origin_facility: string; - origin_facility_object: FacilityModel; - priority: number; - reason: string; - referring_facility_contact_name: string; - referring_facility_contact_number: string; - requested_quantity: number; - status: string; - sub_category: string; - title: string; - assigned_to_object: UserBareMinimum | null; - created_by_object: UserBareMinimum | null; - created_date: string; - last_edited_by_object: UserBareMinimum; - related_patient_object: PatientModel | null; -} - export interface CommentModel { id: string; created_by_object: UserBareMinimum; diff --git a/src/components/Files/FileUpload.tsx b/src/components/Files/FileUpload.tsx index 7a4798c5320..56017294f52 100644 --- a/src/components/Files/FileUpload.tsx +++ b/src/components/Files/FileUpload.tsx @@ -3,7 +3,6 @@ import { ReactNode, useState } from "react"; import { useTranslation } from "react-i18next"; import CareIcon, { IconName } from "@/CAREUI/icons/CareIcon"; -import AuthorizedChild from "@/CAREUI/misc/AuthorizedChild"; import { Button } from "@/components/ui/button"; @@ -19,7 +18,6 @@ import useFileUpload from "@/hooks/useFileUpload"; import { RESULTS_PER_PAGE_LIMIT } from "@/common/constants"; -import { NonReadOnlyUsers } from "@/Utils/AuthorizeFor"; import routes from "@/Utils/request/api"; import useTanStackQueryInstead from "@/Utils/request/useQuery"; @@ -261,91 +259,81 @@ export const FileUpload = (props: FileUploadProps) => { {fileManager.Dialogues} {dischargeSummaryFileManager.Dialogues} {!hideUpload && ( - - {({ isAuthorized }) => - isAuthorized ? ( - <> -

{UPLOAD_HEADING[type]}

- {fileUpload.files[0] ? ( -
-
- - - {fileUpload.files[0].name} - - -
- fileUpload.setFileName(e.value)} - error={fileUpload.error || undefined} - /> -
- - -
- {!!fileUpload.progress && ( - - )} -
- ) : ( -
- {uploadButtons - .filter((b) => b.show !== false) - .map((button, i) => ( - - ))} -
- )} - - ) : ( - <> - ) - } -
+ <> +

{UPLOAD_HEADING[type]}

+ {fileUpload.files[0] ? ( +
+
+ + + {fileUpload.files[0].name} + + +
+ fileUpload.setFileName(e.value)} + error={fileUpload.error || undefined} + /> +
+ + +
+ {!!fileUpload.progress && ( + + )} +
+ ) : ( +
+ {uploadButtons + .filter((b) => b.show !== false) + .map((button, i) => ( + + ))} +
+ )} + )}

{VIEW_HEADING[type]}

@@ -369,11 +357,7 @@ export const FileUpload = (props: FileUploadProps) => { : dischargeSummaryFileManager } associating_id={associatedId} - editable={ - item?.uploaded_by?.username === authUser.username || - authUser.user_type === "DistrictAdmin" || - authUser.user_type === "StateAdmin" - } + editable={item?.uploaded_by?.username === authUser.username} archivable={tab !== "DISCHARGE_SUMMARY"} /> ))} diff --git a/src/components/Form/FieldValidators.tsx b/src/components/Form/FieldValidators.tsx index a09939d8812..de0e8fff0cb 100644 --- a/src/components/Form/FieldValidators.tsx +++ b/src/components/Form/FieldValidators.tsx @@ -1,5 +1,4 @@ export type FieldError = string | undefined; -export type FieldValidator = (value: T, ...args: any) => FieldError; export const RegexValidator = (regex: RegExp, message = "Invalid input") => { return (value: string): FieldError => { diff --git a/src/components/Form/FormFields/Autocomplete.tsx b/src/components/Form/FormFields/Autocomplete.tsx index 37486974881..7e00c89fc7c 100644 --- a/src/components/Form/FormFields/Autocomplete.tsx +++ b/src/components/Form/FormFields/Autocomplete.tsx @@ -11,68 +11,12 @@ import { useTranslation } from "react-i18next"; import CareIcon from "@/CAREUI/icons/CareIcon"; import { DropdownTransition } from "@/components/Common/HelperComponents"; -import FormField from "@/components/Form/FormFields/FormField"; -import { - FormFieldBaseProps, - useFormFieldPropsResolver, -} from "@/components/Form/FormFields/Utils"; import { dropdownOptionClassNames } from "@/components/Form/MultiSelectMenuV2"; import { classNames } from "@/Utils/utils"; type OptionCallback = (option: T) => R; -type AutocompleteFormFieldProps = FormFieldBaseProps & { - placeholder?: string; - options: readonly T[]; - optionLabel: OptionCallback; - optionValue?: OptionCallback; - optionDescription?: OptionCallback; - optionIcon?: OptionCallback; - optionImage?: OptionCallback; - optionDisabled?: OptionCallback; - minQueryLength?: number; - onQuery?: (query: string) => void; - dropdownIcon?: React.ReactNode | undefined; - isLoading?: boolean; - allowRawInput?: boolean; - error?: string; -}; - -const AutocompleteFormField = ( - props: AutocompleteFormFieldProps, -) => { - const field = useFormFieldPropsResolver(props); - return ( - - field.handleChange(value)} - options={props.options} - placeholder={props.placeholder} - optionLabel={props.optionLabel} - optionIcon={props.optionIcon} - optionImage={props.optionImage} - optionValue={props.optionValue} - optionDescription={props.optionDescription} - optionDisabled={props.optionDisabled} - minQueryLength={props.minQueryLength} - onQuery={props.onQuery} - isLoading={props.isLoading} - allowRawInput={props.allowRawInput} - error={field.error} - requiredError={field.error ? props.required : false} - /> - - ); -}; - -export default AutocompleteFormField; - type AutocompleteProps = { id?: string; options: readonly T[]; diff --git a/src/components/Form/Utils.ts b/src/components/Form/Utils.ts index 457c3fc9dd3..21dc9d8b8aa 100644 --- a/src/components/Form/Utils.ts +++ b/src/components/Form/Utils.ts @@ -14,23 +14,3 @@ export type FormReducer = ( prevState: FormState, action: FormAction, ) => FormState; -export type FormDraft = { timestamp: number; form: FormDetails }; - -export const formReducer = ( - state: FormState, - action: FormAction, -): FormState => { - switch (action.type) { - case "set_form": - return { ...state, form: action.form }; - case "set_errors": - return { ...state, errors: action.errors }; - case "set_field": - return { - form: { ...state.form, [action.name]: action.value }, - errors: { ...state.errors, [action.name]: action.error }, - }; - case "set_state": - return action.state; - } -}; diff --git a/src/components/Patient/PatientConsentRecordBlock.tsx b/src/components/Patient/PatientConsentRecordBlock.tsx deleted file mode 100644 index 0378ba95819..00000000000 --- a/src/components/Patient/PatientConsentRecordBlock.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { t } from "i18next"; -import { useEffect, useState } from "react"; - -import { Button } from "@/components/ui/button"; - -import { PatientConsentModel } from "@/components/Facility/models"; -import FileBlock from "@/components/Files/FileBlock"; -import { SelectFormField } from "@/components/Form/FormFields/SelectFormField"; -import { FileUploadModel } from "@/components/Patient/models"; - -import useAuthUser from "@/hooks/useAuthUser"; -import { FileManagerResult } from "@/hooks/useFileManager"; - -import { - CONSENT_PATIENT_CODE_STATUS_CHOICES, - CONSENT_TYPE_CHOICES, -} from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; - -export default function PatientConsentRecordBlockGroup(props: { - consentRecord: PatientConsentModel; - consultationId: string; - fileManager: FileManagerResult; - files?: FileUploadModel[]; -}) { - const { consentRecord, fileManager, files, consultationId } = props; - - const authUser = useAuthUser(); - - const consent = CONSENT_TYPE_CHOICES.find((c) => c.id === consentRecord.type); - const consentPCS = CONSENT_PATIENT_CODE_STATUS_CHOICES.find( - (c) => c.id === consentRecord.patient_code_status, - ); - const [patientCodeStatus, setPatientCodeStatus] = useState(1); - - const handlePCSUpdate = async (status: number) => { - const res = await request(routes.partialUpdateConsent, { - pathParams: { - id: consentRecord.id, - consultationId: consultationId, - }, - body: { - patient_code_status: status, - }, - }); - if (res.data) window.location.reload(); - }; - - useEffect(() => { - if (consentRecord.patient_code_status !== null) - setPatientCodeStatus(consentRecord.patient_code_status); - }, [consentRecord]); - - // see if the user has permission to edit the file. - // only the user who uploaded the file, district admin and state admin can edit the file - const hasEditPermission = (file: FileUploadModel) => - file?.uploaded_by?.username === authUser.username || - authUser.user_type === "DistrictAdmin" || - authUser.user_type === "StateAdmin"; - - return ( -
-
-
-

- {consent?.text} {consentPCS?.text && `(${consentPCS.text})`} -

-
-
- {consentRecord.type === 2 && consentRecord.patient_code_status === 0 && ( -
- setPatientCodeStatus(e.value)} - value={ - CONSENT_PATIENT_CODE_STATUS_CHOICES.find( - (c) => c.id === patientCodeStatus, - )?.id - } - optionValue={(option: any) => option.id} - optionLabel={(option: any) => option.text} - options={CONSENT_PATIENT_CODE_STATUS_CHOICES} - required - error="Please select a patient code status type" - /> - -
- )} - {files?.map((file: FileUploadModel, i: number) => ( - - ))} -
- ); -} diff --git a/src/components/Patient/PatientConsentRecords.tsx b/src/components/Patient/PatientConsentRecords.tsx deleted file mode 100644 index 7d33487c730..00000000000 --- a/src/components/Patient/PatientConsentRecords.tsx +++ /dev/null @@ -1,277 +0,0 @@ -import { t } from "i18next"; -import { Loader2 } from "lucide-react"; -import { useState } from "react"; - -import CareIcon from "@/CAREUI/icons/CareIcon"; - -import { Button, buttonVariants } from "@/components//ui/button"; -import ConfirmDialog from "@/components/Common/ConfirmDialog"; -import Page from "@/components/Common/Page"; -import Tabs from "@/components/Common/Tabs"; -import { PatientConsentModel } from "@/components/Facility/models"; -import { SelectFormField } from "@/components/Form/FormFields/SelectFormField"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; -import PatientConsentRecordBlockGroup from "@/components/Patient/PatientConsentRecordBlock"; - -import useFileManager from "@/hooks/useFileManager"; -import useFileUpload from "@/hooks/useFileUpload"; - -import { - CONSENT_PATIENT_CODE_STATUS_CHOICES, - CONSENT_TYPE_CHOICES, -} from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import request from "@/Utils/request/request"; -import useTanStackQueryInstead from "@/Utils/request/useQuery"; - -export default function PatientConsentRecords(props: { - facilityId: string; - patientId: string; - consultationId: string; -}) { - const { facilityId, patientId, consultationId } = props; - const [showArchived, setShowArchived] = useState(false); - const [showPCSChangeModal, setShowPCSChangeModal] = useState( - null, - ); - const [newConsent, setNewConsent] = useState({ - type: 0, - patient_code_status: 4, - }); - - const fileUpload = useFileUpload({ - type: "CONSENT_RECORD", - allowedExtensions: ["pdf", "jpg", "jpeg", "png"], - allowNameFallback: false, - }); - - const fileManager = useFileManager({ - type: "CONSENT_RECORD", - onArchive: async () => { - refetch(); - }, - onEdit: async () => { - refetch(); - }, - }); - - const { data: consentRecordsData, refetch } = useTanStackQueryInstead( - routes.listConsents, - { - pathParams: { - consultationId, - }, - query: { - limit: 1000, - offset: 0, - }, - }, - ); - - const consentRecords = consentRecordsData?.results; - - const selectField = (name: string) => { - return { - name, - optionValue: (option: any) => option.id, - optionLabel: (option: any) => option.text, - optionDescription: (option: any) => option.desc, - }; - }; - - const handleUpload = async (diffPCS?: PatientConsentModel) => { - const consentExists = consentRecords?.find( - (record) => record.type === newConsent.type && !record.archived, - ); - let consentId = consentExists?.id; - if (!consentExists || diffPCS) { - consentId = undefined; - const res = await request(routes.createConsent, { - pathParams: { consultationId: consultationId }, - body: { - ...newConsent, - patient_code_status: - newConsent.type === 2 ? newConsent.patient_code_status : undefined, - }, - }); - if (res.data) { - consentId = res.data.id; - } - } - consentId && (await fileUpload.handleFileUpload(consentId)); - refetch(); - }; - - return ( - - {fileUpload.Dialogues} - {fileManager.Dialogues} - setShowPCSChangeModal(null)} - onConfirm={() => { - if (showPCSChangeModal !== null) { - handleUpload( - consentRecords?.find( - (record) => - record.type === 2 && - !record.archived && - record.patient_code_status !== showPCSChangeModal, - ), - ); - } - setShowPCSChangeModal(null); - }} - action="Change Patient Code Status" - variant="destructive" - description={`Consent records exist with the "${CONSENT_PATIENT_CODE_STATUS_CHOICES.find((c) => consentRecords?.find((c) => c.type === 2 && !c.archived)?.patient_code_status === c.id)?.text}" patient code status. Adding a new record for a different type will archive the existing records. Are you sure you want to proceed?`} - title="Archive Previous Records" - className="w-auto" - /> - setShowArchived(!!v)} - currentTab={showArchived ? 1 : 0} - /> -
-
-

Add New Record

- { - setNewConsent({ ...newConsent, type: e.value }); - }} - value={newConsent.type} - label="Consent Type" - options={CONSENT_TYPE_CHOICES} - required - /> - {newConsent.type === 2 && ( - { - setNewConsent({ - ...newConsent, - patient_code_status: e.value, - }); - }} - label="Patient Code Status" - value={newConsent.patient_code_status} - options={CONSENT_PATIENT_CODE_STATUS_CHOICES} - required - /> - )} - fileUpload.setFileName(e.value)} - /> -
- {fileUpload.files[0] ? ( - <> - - - - ) : ( - <> - - - - )} -
-
{fileUpload.error}
-
-
- {consentRecords?.filter( - (r) => - r.files?.filter( - (f) => - f.associating_id === r.id && f.is_archived === showArchived, - ).length, - ).length === 0 ? ( -
- No consent records found -
- ) : ( - !consentRecords && ( -
- ) - )} -
- {consentRecords?.map((record, index) => ( - - f.associating_id === record.id && - showArchived === f.is_archived, - )} - /> - ))} -
-
-
- - ); -} diff --git a/src/components/Users/ConfirmFacilityModal.tsx b/src/components/Users/ConfirmFacilityModal.tsx deleted file mode 100644 index 851f685fbb9..00000000000 --- a/src/components/Users/ConfirmFacilityModal.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useTranslation } from "react-i18next"; - -import ConfirmDialog from "@/components/Common/ConfirmDialog"; -import { FacilityModel } from "@/components/Facility/models"; - -const ConfirmFacilityModal = ({ - username, - currentFacility, - homeFacility, - handleCancel, - handleOk, - type, -}: { - username: string; - currentFacility?: FacilityModel; - homeFacility?: FacilityModel; - handleCancel: () => void; - handleOk: () => void; - type: string; -}) => { - const { t } = useTranslation(); - const title = t(type); - let action = ""; - let body; - switch (type) { - case "unlink_facility": - action = "Unlink"; - body = ( -
-
- {t("unlink_facility_confirm")}{" "} - {currentFacility?.name} {t("from_user")}{" "} - {username} ? -
- {t("unlink_facility_access")} -
-
- ); - break; - case "clear_home_facility": - action = "Clear"; - body = ( -
-
- {t("clear_home_facility_confirm")}{" "} - {currentFacility?.name} {t("from_user")}{" "} - {username} ? -
-
-
- ); - break; - case "replace_home_facility": - action = "Replace"; - body = ( -
- {t("replace_home_facility_confirm")}{" "} - {homeFacility?.name} {t("with")}{" "} - {currentFacility?.name}{" "} - {t("replace_home_facility_confirm_as")} {username}? -
- ); - break; - } - return ( - {title}} - show={true} - action={action} - onClose={handleCancel} - onConfirm={handleOk} - variant="destructive" - > -
{body}
-
- ); -}; - -export default ConfirmFacilityModal; diff --git a/src/components/Users/ProfileComponents.tsx b/src/components/Users/ProfileComponents.tsx deleted file mode 100644 index b58a602bde9..00000000000 --- a/src/components/Users/ProfileComponents.tsx +++ /dev/null @@ -1,21 +0,0 @@ -export const LabelValueCard = (props: { children: React.ReactNode }) => ( -
{props.children}
-); - -export const ProfileLabel = (props: { text: string }) => ( -
- {props.text} -
-); - -export const ProfileValue = (props: { text: string }) => ( -
- {props.text || "-"} -
-); - -export const ValueBadge = (props: { text: string }) => ( -
- {props.text} -
-); diff --git a/src/components/Users/UnlinkFacilityDialog.tsx b/src/components/Users/UnlinkFacilityDialog.tsx deleted file mode 100644 index f0ba9c65704..00000000000 --- a/src/components/Users/UnlinkFacilityDialog.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useState } from "react"; - -import ConfirmDialog from "@/components/Common/ConfirmDialog"; - -interface ConfirmDialogProps { - facilityName: string; - userName: string; - isHomeFacility: boolean; - handleCancel: () => void; - handleOk: () => void; -} - -const UnlinkFacilityDialog = (props: ConfirmDialogProps) => { - const { facilityName, userName, isHomeFacility, handleCancel, handleOk } = - props; - - const [disable, setDisable] = useState(false); - - const handleSubmit = () => { - handleOk(); - setDisable(true); - }; - return ( - - {isHomeFacility ? "Clear Home Facility" : "Unlink User Facility"} - - } - show={true} - action={isHomeFacility ? "Clear" : "Unlink"} - onClose={handleCancel} - onConfirm={handleSubmit} - disabled={disable} - variant="destructive" - > -
-
- Are you sure you want to{" "} - {isHomeFacility ? "clear the home facility" : "unlink the facility"}{" "} - {facilityName} from user {userName}{" "} - ? -
- {!isHomeFacility && "The user will lose access to the facility."} -
-
-
- ); -}; - -export default UnlinkFacilityDialog; diff --git a/src/components/Users/UnlinkSkillDialog.tsx b/src/components/Users/UnlinkSkillDialog.tsx deleted file mode 100644 index 6ddfcc6e569..00000000000 --- a/src/components/Users/UnlinkSkillDialog.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useState } from "react"; -import { useTranslation } from "react-i18next"; - -import ConfirmDialog from "@/components/Common/ConfirmDialog"; - -interface Props { - skillName: string; - userName: string; - onCancel: () => void; - onSubmit: () => void; -} - -export default function UnlinkSkillDialog(props: Props) { - const [disabled, setDisabled] = useState(false); - const { t } = useTranslation(); - - const handleSubmit = () => { - props.onSubmit(); - setDisabled(true); - }; - - return ( - - - {t("unlink_skill_confirm")} {props.skillName}{" "} - {t("from_user")} {props.userName}?{" "} - {t("unlink_skill_access")} - -
- } - >
- ); -} diff --git a/src/components/Users/UserFilter.tsx b/src/components/Users/UserFilter.tsx deleted file mode 100644 index 95a365727ee..00000000000 --- a/src/components/Users/UserFilter.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import { t } from "i18next"; -import { toast } from "sonner"; - -import FiltersSlideover from "@/CAREUI/interactive/FiltersSlideover"; - -import { FieldLabel } from "@/components/Form/FormFields/FormField"; -import PhoneNumberFormField from "@/components/Form/FormFields/PhoneNumberFormField"; -import TextFormField from "@/components/Form/FormFields/TextFormField"; -import SelectMenuV2 from "@/components/Form/SelectMenuV2"; - -import useMergeState from "@/hooks/useMergeState"; - -import { - USER_LAST_ACTIVE_OPTIONS, - USER_TYPE_OPTIONS, -} from "@/common/constants"; - -import routes from "@/Utils/request/api"; -import useTanStackQueryInstead from "@/Utils/request/useQuery"; -import { parsePhoneNumber } from "@/Utils/utils"; - -const parsePhoneNumberForFilterParam = (phoneNumber: string) => { - if (!phoneNumber) return ""; - if (phoneNumber === "+91") return ""; - if (phoneNumber.startsWith("+")) return parsePhoneNumber(phoneNumber) ?? ""; - return phoneNumber; -}; - -export default function UserFilter(props: any) { - const { filter, onChange, closeFilter, removeFilters } = props; - const [filterState, setFilterState] = useMergeState({ - first_name: filter.first_name || "", - last_name: filter.last_name || "", - phone_number: filter.phone_number || "+91", - alt_phone_number: filter.alt_phone_number || "+91", - user_type: filter.user_type || "", - district: filter.district || "", - state: filter.state || "", - home_facility: filter.home_facility || "", - home_facility_ref: null, - last_active_days: filter.last_active_days || "", - }); - - useTanStackQueryInstead(routes.getAnyFacility, { - pathParams: { id: filter.home_facility }, - prefetch: !!filter.home_facility && filter.home_facility !== "NONE", - onResponse: ({ data }) => setFilterState({ home_facility_ref: data }), - }); - - const applyFilter = () => { - const { - first_name, - last_name, - phone_number, - alt_phone_number, - user_type, - district, - state, - home_facility, - last_active_days, - } = filterState; - const data = { - first_name: first_name || "", - last_name: last_name || "", - phone_number: parsePhoneNumberForFilterParam(phone_number), - alt_phone_number: parsePhoneNumberForFilterParam(alt_phone_number), - user_type: user_type || "", - district: district || "", - state: district ? state || "" : "", - home_facility: home_facility || "", - last_active_days: last_active_days || "", - }; - if (state && !district) { - toast.warning(t("district_is_required_when_state_is_selected")); - return; - } - onChange(data); - }; - - const handleChange = ({ name, value }: any) => { - if (name === "state" && !value) - setFilterState({ ...filterState, state: value, district: undefined }); - else setFilterState({ ...filterState, [name]: value }); - }; - - return ( - { - removeFilters(); - closeFilter(); - }} - > - - - -
- Role - o.role + ((o.readOnly && " (Read Only)") || "")} - optionValue={(o) => o.id} - value={filterState.user_type} - onChange={(v) => setFilterState({ ...filterState, user_type: v })} - /> -
- -
- Active in last... - o.text} - optionValue={(o) => o.id} - value={filterState.last_active_days} - onChange={(v) => - setFilterState({ ...filterState, last_active_days: v }) - } - /> -
- -
- -
-
- -
-
- ); -} diff --git a/src/components/Users/UserFormValidations.tsx b/src/components/Users/UserFormValidations.tsx index d0a6c06402b..4899a6f3d25 100644 --- a/src/components/Users/UserFormValidations.tsx +++ b/src/components/Users/UserFormValidations.tsx @@ -6,51 +6,6 @@ export type UserType = "doctor" | "nurse" | "staff" | "volunteer"; export type Gender = "male" | "female" | "non_binary" | "transgender"; -export type UserForm = { - user_type?: UserType; - gender: Gender; - password?: string; - c_password?: string; - geo_organization?: string; - username?: string; - first_name: string; - last_name: string; - email: string; - phone_number: string; -}; - -export const newUserFields: Array = [ - "user_type", - "username", - "password", - "c_password", - "first_name", - "last_name", - "email", - "phone_number", - "gender", - "geo_organization", -]; - -export const editUserFields: Array = [ - "first_name", - "last_name", - "gender", - "email", - "phone_number", -]; - -export const editBasicInfoFields: Array = [ - "first_name", - "last_name", - "gender", -]; - -export const editContactInfoFields: Array = [ - "email", - "phone_number", -]; - export const validateRule = ( isConditionMet: boolean, initialMessage: JSX.Element | string, diff --git a/src/components/Users/UserListAndCard.tsx b/src/components/Users/UserListAndCard.tsx index f6a7e1fb7a7..b1123f55d08 100644 --- a/src/components/Users/UserListAndCard.tsx +++ b/src/components/Users/UserListAndCard.tsx @@ -14,8 +14,6 @@ import useAuthUser from "@/hooks/useAuthUser"; import useSlug from "@/hooks/useSlug"; import useWindowDimensions from "@/hooks/useWindowDimensions"; -import { USER_TYPE_OPTIONS } from "@/common/constants"; - import { formatName, formatPhoneNumber, @@ -24,33 +22,6 @@ import { } from "@/Utils/utils"; import { UserBase } from "@/types/user/user"; -export const GetUserTypes = () => { - const authUser = useAuthUser(); - const defaultAllowedUserTypes = USER_TYPE_OPTIONS; - - // Superuser gets all options - if (authUser.is_superuser) { - return [...USER_TYPE_OPTIONS]; - } - - switch (authUser.user_type) { - // case "StaffReadOnly": - // return readOnlyUsers.slice(0, 1); - // case "DistrictReadOnlyAdmin": - // return readOnlyUsers.slice(0, 2); - // case "StateReadOnlyAdmin": - // return readOnlyUsers.slice(0, 3); - // case "Pharmacist": - // return USER_TYPE_OPTIONS.slice(0, 1); - // case "Nurse": - // case "Staff": - // if (editForm) return [...defaultAllowedUserTypes]; - // // Temporarily allows creation of users with elevated permissions due to introduction of new roles. - // return [...defaultAllowedUserTypes, USER_TYPE_OPTIONS[6]]; - default: - } - return defaultAllowedUserTypes; -}; const GetDetailsButton = (username: string) => { const { t } = useTranslation(); const facilityId = useSlug("facility"); diff --git a/src/components/Users/UserSummary.tsx b/src/components/Users/UserSummary.tsx index 4cb198528df..6c0e8fb0073 100644 --- a/src/components/Users/UserSummary.tsx +++ b/src/components/Users/UserSummary.tsx @@ -4,7 +4,6 @@ import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import CareIcon from "@/CAREUI/icons/CareIcon"; -import AuthorizedChild from "@/CAREUI/misc/AuthorizedChild"; import LanguageSelector from "@/components/Common/LanguageSelector"; import UserColumns from "@/components/Common/UserColumns"; @@ -170,20 +169,16 @@ export default function UserSummaryTab({
- deletePermitted}> - {({ isAuthorized }) => ( - - )} - +
)} diff --git a/src/components/Users/models.tsx b/src/components/Users/models.tsx index 4ea4b720fde..5335255c298 100644 --- a/src/components/Users/models.tsx +++ b/src/components/Users/models.tsx @@ -1,13 +1,10 @@ -import { GENDER_TYPES, UserRole } from "@/common/constants"; +import { Gender, UserType } from "@/components/Users/UserFormValidations"; + +import { GENDER_TYPES } from "@/common/constants"; import { FeatureFlag } from "@/Utils/featureFlags"; import { Organization } from "@/types/organization/organization"; -interface HomeFacilityObjectModel { - id?: string; - name?: string; -} - export type UpdatePasswordForm = { old_password: string; username: string; @@ -20,14 +17,12 @@ export type UserBareMinimum = { first_name: string; last_name: string; email: string; - user_type: UserRole; + user_type: UserType; last_login: string | undefined; read_profile_picture_url?: string; external_id: string; }; -export type GenderType = "Male" | "Female" | "Transgender"; - export type UserFacilityModel = { id: string; name: string; @@ -41,13 +36,12 @@ export type UserModel = UserBareMinimum & { video_connect_link: string; phone_number?: string; alt_phone_number?: string; - gender?: GenderType; + gender?: Gender; read_profile_picture_url?: string; date_of_birth: Date | null | string; is_superuser?: boolean; verified?: boolean; home_facility?: string; - home_facility_object?: HomeFacilityObjectModel; qualification?: string; doctor_experience_commenced_on?: string; doctor_medical_council_registration?: string; @@ -58,27 +52,6 @@ export type UserModel = UserBareMinimum & { permissions: string[]; }; -export type UserBaseModel = { - email: string; - first_name: string; - last_name: string; - id: number; - user_type: UserRole; - username: string; - last_login: string | undefined; -}; - -export interface SkillObjectModel { - id: string; - name: string; - description?: string; -} - -export interface SkillModel { - id: string; - skill_object: SkillObjectModel; -} - export interface UserAssignedModel extends UserBareMinimum { local_body?: number; district?: number; @@ -91,10 +64,8 @@ export interface UserAssignedModel extends UserBareMinimum { is_superuser?: boolean; verified?: boolean; home_facility?: string; - home_facility_object?: HomeFacilityObjectModel; qualification?: string; doctor_experience_commenced_on?: Date; doctor_medical_council_registration?: string; weekly_working_hours?: string; - skills: SkillObjectModel[]; } diff --git a/src/hooks/useCanvas.ts b/src/hooks/useCanvas.ts deleted file mode 100644 index 6d3e2206f9e..00000000000 --- a/src/hooks/useCanvas.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { useEffect, useRef } from "react"; - -export default function useCanvas() { - const canvasRef = useRef(null); - const contextRef = useRef(null); - - useEffect(() => { - const canvas = canvasRef.current; - const context = canvas?.getContext("2d"); - contextRef.current = context as CanvasRenderingContext2D; - }, []); - - return { canvasRef, contextRef }; -} diff --git a/src/hooks/useFullscreen.ts b/src/hooks/useFullscreen.ts deleted file mode 100644 index a826ff5f0ad..00000000000 --- a/src/hooks/useFullscreen.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { useEffect, useState } from "react"; - -interface HTMLElementWithFullscreen extends HTMLElement { - webkitEnterFullscreen?: () => void; - webkitExitFullscreen?: () => void; -} - -export default function useFullscreen() { - const [isFullscreen, _setIsFullscreen] = useState( - !!document.fullscreenElement, - ); - - useEffect(() => { - const onFullscreenChange = () => - _setIsFullscreen(!!document.fullscreenElement); - - document.addEventListener("fullscreenchange", onFullscreenChange); - - return () => - document.removeEventListener("fullscreenchange", onFullscreenChange); - }, []); - - function openFullscreen(elem: HTMLElementWithFullscreen) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (elem.webkitEnterFullscreen) - elem.webkitEnterFullscreen(); // Safari - else elem.requestFullscreen(); - } - - function exitFullscreen(elem: HTMLElementWithFullscreen) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - if (elem.webkitExitFullscreen) - elem.webkitExitFullscreen(); // Safari - else document.exitFullscreen(); - } - - const setFullscreen = ( - value: boolean, - element?: HTMLElement, - enterLandscape?: boolean, - ) => { - const fullscreenElement = element ?? document.documentElement; - - if (value) { - openFullscreen(fullscreenElement); - if (enterLandscape) { - (screen.orientation as any)?.lock?.("landscape"); - } - } else { - exitFullscreen(fullscreenElement); - } - }; - - return [isFullscreen, setFullscreen] as const; -} diff --git a/src/hooks/useIsAuthorized.ts b/src/hooks/useIsAuthorized.ts deleted file mode 100644 index 8c8e24a0eb7..00000000000 --- a/src/hooks/useIsAuthorized.ts +++ /dev/null @@ -1,8 +0,0 @@ -import useAuthUser from "@/hooks/useAuthUser"; - -import { AuthorizedForCB } from "@/Utils/AuthorizeFor"; - -export const useIsAuthorized = (authorizeFor: AuthorizedForCB) => { - const authUser = useAuthUser(); - return authorizeFor(authUser.user_type); -}; diff --git a/src/hooks/useMessageListener.ts b/src/hooks/useMessageListener.ts deleted file mode 100644 index 9dba3a18e58..00000000000 --- a/src/hooks/useMessageListener.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useEffect } from "react"; - -type onMessage = (data: any) => void; - -export const useMessageListener = (onMessage: onMessage) => { - useEffect(() => { - const handleMessage = (e: MessageEvent) => { - onMessage?.(e.data); - }; - navigator.serviceWorker?.addEventListener?.("message", handleMessage); - - return () => { - navigator.serviceWorker?.removeEventListener?.("message", handleMessage); - }; - }); -}; diff --git a/src/hooks/useRangePagination.ts b/src/hooks/useRangePagination.ts deleted file mode 100644 index 966596ffb33..00000000000 --- a/src/hooks/useRangePagination.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { useEffect, useMemo, useState } from "react"; - -interface DateRange { - start: Date; - end: Date; -} - -interface Props { - bounds: DateRange; - perPage: number; - slots?: number; - defaultEnd?: boolean; -} - -const useRangePagination = ({ bounds, perPage, ...props }: Props) => { - const [currentRange, setCurrentRange] = useState( - getInitialBounds(bounds, perPage, props.defaultEnd), - ); - - useEffect(() => { - setCurrentRange(getInitialBounds(bounds, perPage, props.defaultEnd)); - }, [JSON.stringify(bounds), perPage, props.defaultEnd]); - - const next = () => { - const { end } = currentRange; - const deltaBounds = bounds.end.valueOf() - bounds.start.valueOf(); - const deltaCurrent = end.valueOf() - bounds.start.valueOf(); - - if (deltaCurrent + perPage > deltaBounds) { - setCurrentRange({ - start: new Date(bounds.end.valueOf() - perPage), - end: bounds.end, - }); - } else { - setCurrentRange({ - start: new Date(end.valueOf()), - end: new Date(end.valueOf() + perPage), - }); - } - }; - - const previous = () => { - const { start } = currentRange; - const deltaCurrent = start.valueOf() - bounds.start.valueOf(); - - if (deltaCurrent - perPage < 0) { - setCurrentRange({ - start: bounds.start, - end: new Date(bounds.start.valueOf() + perPage), - }); - } else { - setCurrentRange({ - start: new Date(start.valueOf() - perPage), - end: new Date(start.valueOf()), - }); - } - }; - - const slots = useMemo(() => { - if (!props.slots) { - return []; - } - - const slots: DateRange[] = []; - const { start } = currentRange; - const delta = perPage / props.slots; - - for (let i = 0; i < props.slots; i++) { - slots.push({ - start: new Date(start.valueOf() + delta * i), - end: new Date(start.valueOf() + delta * (i + 1)), - }); - } - - return slots; - }, [currentRange, props.slots, perPage]); - - return { - currentRange, - hasNext: currentRange.end < bounds.end, - hasPrevious: currentRange.start > bounds.start, - previous, - next, - slots, - }; -}; - -export default useRangePagination; - -const getInitialBounds = ( - bounds: DateRange, - perPage: number, - defaultEnd?: boolean, -) => { - const deltaBounds = bounds.end.valueOf() - bounds.start.valueOf(); - - if (deltaBounds < perPage) { - return bounds; - } - - if (defaultEnd) { - return { - start: new Date(bounds.end.valueOf() - perPage), - end: bounds.end, - }; - } - - return { - start: bounds.start, - end: new Date(bounds.start.valueOf() + perPage), - }; -}; diff --git a/src/hooks/useVisibility.ts b/src/hooks/useVisibility.ts deleted file mode 100644 index 252d4174711..00000000000 --- a/src/hooks/useVisibility.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { MutableRefObject, useEffect, useRef, useState } from "react"; - -/** - * Check if an element is in viewport - * @param {number} offset - Number of pixels up to the observable element from the top - * - * **Usage Example:** - * ```tsx - * const [isVisible, elementRef] = useVisibility(-200); - * - * useEffect(() => { - * // do something. - * }, [isVisible]); - * - * return ( - * ... - * - * ... - * ); - * ``` - */ -export default function useVisibility( - offset = 0, -): [boolean, MutableRefObject] { - const [isVisible, setIsVisible] = useState(false); - const currentElement = useRef(); - - const onScroll = () => { - if (!currentElement.current) { - setIsVisible(false); - return; - } - const top = currentElement.current.getBoundingClientRect().top; - setIsVisible(top + offset >= 0 && top - offset <= window.innerHeight); - }; - - useEffect(() => { - onScroll(); - document.addEventListener("scroll", onScroll, true); - return () => document.removeEventListener("scroll", onScroll, true); - }, [currentElement.current]); - - return [isVisible, currentElement]; -} diff --git a/src/pages/Facility/Utils.tsx b/src/pages/Facility/Utils.tsx index 05aee81179a..f30ae8777c6 100644 --- a/src/pages/Facility/Utils.tsx +++ b/src/pages/Facility/Utils.tsx @@ -4,20 +4,8 @@ import CareIcon from "@/CAREUI/icons/CareIcon"; import { Badge } from "@/components/ui/badge"; -import { SkillObjectModel, UserAssignedModel } from "@/components/Users/models"; - import { FACILITY_FEATURE_TYPES } from "@/common/constants"; -export interface DoctorModel - extends Omit, "skills"> { - role: string; - education: string; - experience: string; - languages: string[]; - read_profile_picture_url: string; - skills: SkillObjectModel[]; -} - export const FeatureBadge = ({ featureId }: { featureId: number }) => { const feature = FACILITY_FEATURE_TYPES.find((f) => f.id === featureId); if (!feature) {