diff --git a/.example.env b/.example.env index dd4a8865878..127db15e710 100644 --- a/.example.env +++ b/.example.env @@ -69,7 +69,6 @@ REACT_STILL_WATCHING_PROMPT_DURATION= # Feature flags REACT_ENABLE_HCX=true REACT_ENABLE_ABDM=true -REACT_ENABLE_SCRIBE=true REACT_WARTIME_SHIFTING=true # JWT token refresh interval (in milliseconds) (default: 5 minutes) diff --git a/care.config.ts b/care.config.ts index 4341a03dd48..e3effeca1b0 100644 --- a/care.config.ts +++ b/care.config.ts @@ -103,10 +103,6 @@ const careConfig = { abdm: { enabled: (env.REACT_ENABLE_ABDM ?? "true") === "true", }, - - scribe: { - enabled: env.REACT_ENABLE_SCRIBE === "true", - }, } as const; export default careConfig; diff --git a/src/App.tsx b/src/App.tsx index 2e7f185f80b..6c6d5255b4d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,10 +1,11 @@ import { Suspense } from "react"; import Routers from "./Routers"; import ThemedFavicon from "./CAREUI/misc/ThemedFavicon"; -import Intergrations from "./Integrations"; +import Integrations from "./Integrations"; import Loading from "./Components/Common/Loading"; import HistoryAPIProvider from "./Providers/HistoryAPIProvider"; import AuthUserProvider from "./Providers/AuthUserProvider"; +import { FeatureFlagsProvider } from "./Utils/featureFlags"; const App = () => { return ( @@ -12,12 +13,14 @@ const App = () => { }> - + + + {/* Integrations */} - - + + ); diff --git a/src/Components/Facility/models.tsx b/src/Components/Facility/models.tsx index 97d81674658..56c70f2ac94 100644 --- a/src/Components/Facility/models.tsx +++ b/src/Components/Facility/models.tsx @@ -7,6 +7,7 @@ import { PATIENT_NOTES_THREADS, UserRole, } from "../../Common/constants"; +import { FeatureFlag } from "../../Utils/featureFlags"; import { ConsultationDiagnosis, CreateDiagnosis } from "../Diagnosis/types"; import { AssignedToObjectModel, @@ -80,6 +81,7 @@ export interface FacilityModel { local_body?: number; ward?: number; pincode?: string; + facility_flags?: FeatureFlag[]; latitude?: string; longitude?: string; kasp_empanelled?: boolean; diff --git a/src/Components/Patient/DailyRounds.tsx b/src/Components/Patient/DailyRounds.tsx index c0cd7fe803c..ef1281fb62b 100644 --- a/src/Components/Patient/DailyRounds.tsx +++ b/src/Components/Patient/DailyRounds.tsx @@ -542,6 +542,7 @@ export const DailyRounds = (props: any) => { >
{ setDiagnosisSuggestions([]); diff --git a/src/Components/Scribe/Scribe.tsx b/src/Components/Scribe/Scribe.tsx index d952ff7b1b2..384bf519a04 100644 --- a/src/Components/Scribe/Scribe.tsx +++ b/src/Components/Scribe/Scribe.tsx @@ -7,8 +7,8 @@ import * as Notify from "../../Utils/Notifications"; import request from "../../Utils/request/request"; import { UserModel } from "../Users/models"; import useSegmentedRecording from "../../Utils/useSegmentedRecorder"; -import careConfig from "@careConfig"; import uploadFile from "../../Utils/request/uploadFile"; +import { useFeatureFlags } from "../../Utils/featureFlags"; interface FieldOption { id: string | number; @@ -52,6 +52,7 @@ export type ScribeModel = { }; interface ScribeProps { + facilityId: string; form: ScribeForm; existingData?: { [key: string]: any }; onFormUpdate: (fields: any) => void; @@ -62,7 +63,11 @@ const SCRIBE_FILE_TYPES = { SCRIBE: 1, }; -export const Scribe: React.FC = ({ form, onFormUpdate }) => { +export const Scribe: React.FC = ({ + form, + onFormUpdate, + facilityId, +}) => { const [open, setOpen] = useState(false); const [_progress, setProgress] = useState(0); const [stage, setStage] = useState("start"); @@ -80,6 +85,8 @@ export const Scribe: React.FC = ({ form, onFormUpdate }) => { const stageRef = useRef(stage); const [fields, setFields] = useState([]); + const featureFlags = useFeatureFlags(facilityId); + useEffect(() => { const loadFields = async () => { const fields = await form.fields(); @@ -544,7 +551,7 @@ export const Scribe: React.FC = ({ form, onFormUpdate }) => { } } - if (!careConfig.scribe.enabled) return null; + if (!featureFlags.includes("SCRIBE_ENABLED")) return null; return ( diff --git a/src/Components/Users/models.tsx b/src/Components/Users/models.tsx index 1bbe494b9ed..790826022b5 100644 --- a/src/Components/Users/models.tsx +++ b/src/Components/Users/models.tsx @@ -1,4 +1,5 @@ import { GENDER_TYPES, UserRole } from "../../Common/constants"; +import { FeatureFlag } from "../../Utils/featureFlags"; import { DistrictModel, LocalBodyModel, StateModel } from "../Facility/models"; interface HomeFacilityObjectModel { @@ -44,6 +45,7 @@ export type UserModel = UserBareMinimum & { doctor_experience_commenced_on?: string; doctor_medical_council_registration?: string; weekly_working_hours?: string | null; + user_flags?: FeatureFlag[]; }; export type UserBaseModel = { diff --git a/src/Integrations/index.tsx b/src/Integrations/index.tsx index aeb0399a452..9b2b1e156fd 100644 --- a/src/Integrations/index.tsx +++ b/src/Integrations/index.tsx @@ -1,6 +1,6 @@ import Sentry from "./Sentry"; import Plausible from "./Plausible"; -const Intergrations = { Sentry, Plausible }; +const Integrations = { Sentry, Plausible }; -export default Intergrations; +export default Integrations; diff --git a/src/Utils/featureFlags.tsx b/src/Utils/featureFlags.tsx new file mode 100644 index 00000000000..739d49e821a --- /dev/null +++ b/src/Utils/featureFlags.tsx @@ -0,0 +1,78 @@ +import { createContext, useContext, useState, useEffect } from "react"; +import useQuery from "./request/useQuery"; +import routes from "../Redux/api"; +import useAuthUser from "../Common/hooks/useAuthUser"; +import { FacilityModel } from "../Components/Facility/models"; + +export type FeatureFlag = "SCRIBE_ENABLED"; // "HCX_ENABLED" | "ABDM_ENABLED" | + +export interface FeatureFlagsResponse { + user_flags: FeatureFlag[]; + facility_flags: { + facility: string; + features: FeatureFlag[]; + }[]; +} + +const defaultFlags: FeatureFlag[] = []; + +const FeatureFlagsContext = createContext({ + user_flags: defaultFlags, + facility_flags: [], +}); + +export const FeatureFlagsProvider = (props: { children: React.ReactNode }) => { + const [featureFlags, setFeatureFlags] = useState({ + user_flags: defaultFlags, + facility_flags: [], + }); + + const user = useAuthUser(); + + useEffect(() => { + if (user.user_flags) { + setFeatureFlags((ff) => ({ + ...ff, + user_flags: [...defaultFlags, ...(user.user_flags || [])], + })); + } + }, [user]); + + return ( + + {props.children} + + ); +}; + +export const useFeatureFlags = (facility?: FacilityModel | string) => { + const [facilityObject, setFacilityObject] = useState< + FacilityModel | undefined + >(typeof facility === "string" ? undefined : facility); + + const context = useContext(FeatureFlagsContext); + if (context === undefined) { + throw new Error( + "useFeatureFlags must be used within a FeatureFlagsProvider", + ); + } + + const facilityQuery = useQuery(routes.getPermittedFacility, { + pathParams: { + id: typeof facility === "string" ? facility : "", + }, + prefetch: false, + silent: true, + onResponse: (res) => { + setFacilityObject(res.data); + }, + }); + + const facilityFlags = facilityObject?.facility_flags || []; + + useEffect(() => { + facilityQuery.refetch(); + }, [facility]); + + return [...context.user_flags, ...facilityFlags]; +};