Skip to content

Commit

Permalink
Enable features from backend feature flags (#8512)
Browse files Browse the repository at this point in the history
  • Loading branch information
shivankacker authored Oct 3, 2024
1 parent ed31b71 commit cf8f87b
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 14 deletions.
1 change: 0 additions & 1 deletion .example.env
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 0 additions & 4 deletions care.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
11 changes: 7 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
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 (
<Suspense fallback={<Loading />}>
<ThemedFavicon />
<HistoryAPIProvider>
<AuthUserProvider unauthorized={<Routers.SessionRouter />}>
<Routers.AppRouter />
<FeatureFlagsProvider>
<Routers.AppRouter />
</FeatureFlagsProvider>
</AuthUserProvider>

{/* Integrations */}
<Intergrations.Sentry disabled={!import.meta.env.PROD} />
<Intergrations.Plausible />
<Integrations.Sentry disabled={!import.meta.env.PROD} />
<Integrations.Plausible />
</HistoryAPIProvider>
</Suspense>
);
Expand Down
2 changes: 2 additions & 0 deletions src/Components/Facility/models.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -80,6 +81,7 @@ export interface FacilityModel {
local_body?: number;
ward?: number;
pincode?: string;
facility_flags?: FeatureFlag[];
latitude?: string;
longitude?: string;
kasp_empanelled?: boolean;
Expand Down
1 change: 1 addition & 0 deletions src/Components/Patient/DailyRounds.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,7 @@ export const DailyRounds = (props: any) => {
>
<div className="flex w-full justify-end md:m-4">
<Scribe
facilityId={facilityId}
form={SCRIBE_FORMS.daily_round}
onFormUpdate={async (fields) => {
setDiagnosisSuggestions([]);
Expand Down
13 changes: 10 additions & 3 deletions src/Components/Scribe/Scribe.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -52,6 +52,7 @@ export type ScribeModel = {
};

interface ScribeProps {
facilityId: string;
form: ScribeForm;
existingData?: { [key: string]: any };
onFormUpdate: (fields: any) => void;
Expand All @@ -62,7 +63,11 @@ const SCRIBE_FILE_TYPES = {
SCRIBE: 1,
};

export const Scribe: React.FC<ScribeProps> = ({ form, onFormUpdate }) => {
export const Scribe: React.FC<ScribeProps> = ({
form,
onFormUpdate,
facilityId,
}) => {
const [open, setOpen] = useState(false);
const [_progress, setProgress] = useState(0);
const [stage, setStage] = useState("start");
Expand All @@ -80,6 +85,8 @@ export const Scribe: React.FC<ScribeProps> = ({ form, onFormUpdate }) => {
const stageRef = useRef(stage);
const [fields, setFields] = useState<Field[]>([]);

const featureFlags = useFeatureFlags(facilityId);

useEffect(() => {
const loadFields = async () => {
const fields = await form.fields();
Expand Down Expand Up @@ -544,7 +551,7 @@ export const Scribe: React.FC<ScribeProps> = ({ form, onFormUpdate }) => {
}
}

if (!careConfig.scribe.enabled) return null;
if (!featureFlags.includes("SCRIBE_ENABLED")) return null;

return (
<Popover>
Expand Down
2 changes: 2 additions & 0 deletions src/Components/Users/models.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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 = {
Expand Down
4 changes: 2 additions & 2 deletions src/Integrations/index.tsx
Original file line number Diff line number Diff line change
@@ -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;
78 changes: 78 additions & 0 deletions src/Utils/featureFlags.tsx
Original file line number Diff line number Diff line change
@@ -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<FeatureFlagsResponse>({
user_flags: defaultFlags,
facility_flags: [],
});

export const FeatureFlagsProvider = (props: { children: React.ReactNode }) => {
const [featureFlags, setFeatureFlags] = useState<FeatureFlagsResponse>({
user_flags: defaultFlags,
facility_flags: [],
});

const user = useAuthUser();

useEffect(() => {
if (user.user_flags) {
setFeatureFlags((ff) => ({
...ff,
user_flags: [...defaultFlags, ...(user.user_flags || [])],
}));
}
}, [user]);

return (
<FeatureFlagsContext.Provider value={featureFlags}>
{props.children}
</FeatureFlagsContext.Provider>
);
};

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];
};

0 comments on commit cf8f87b

Please sign in to comment.