Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable features from backend feature flags #8512

Merged
merged 7 commits into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
rithviknishad marked this conversation as resolved.
Show resolved Hide resolved
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) => {
rithviknishad marked this conversation as resolved.
Show resolved Hide resolved
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];
};
Loading