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/README.md b/README.md
index fd5328150f2..73ce06815d1 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,10 @@ Authenticate to staging API with any of the following credentials
- Once the code review is done, the PR will be marked with a "Needs Testing" label where it'll be queued for QA testing.
- Once tested, the PR would be marked with a "Tested" label and would be queued for merge.
+### Translations
+
+All strings must be encased in i18n translations. New translation strings must be specified in `src`->`Locale`->`en`. Do not add translations for languages other than english through pull requests. Other language translations can be contributed through [Crowdin](https://crowdin.com/project/ohccarefe)
+
### Testing
To ensure the quality of our pull requests, we use a variety of tools:
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/AddBedForm.tsx b/src/Components/Facility/AddBedForm.tsx
index 0a612fa776b..047549bd652 100644
--- a/src/Components/Facility/AddBedForm.tsx
+++ b/src/Components/Facility/AddBedForm.tsx
@@ -127,7 +127,8 @@ export const AddBedForm = ({ facilityId, locationId, bedId }: Props) => {
const { res } = await request(routes.createFacilityBed, {
body: { ...data, facility: facilityId, location: locationId },
});
- res?.ok && onSuccess("Bed(s) created successfully");
+ res?.ok &&
+ onSuccess(t("bed_created_notification", { count: numberOfBeds }));
}
};
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/Locale/TRANSLATION_CONTRIBUTION.md b/src/Locale/TRANSLATION_CONTRIBUTION.md
index c262215c643..f3150ae8fc0 100644
--- a/src/Locale/TRANSLATION_CONTRIBUTION.md
+++ b/src/Locale/TRANSLATION_CONTRIBUTION.md
@@ -1,29 +1,13 @@
-# Contributing Translation
+# Contributing Translations
-### For adding a new language
-
-
+## Adding a new language
- Open the Terminal and `cd` to `care_fe/src/Locale`
- Run the command `node update_locale.js `
Eg: `node update_locale.js ml` for Malayalam
-- The command will create a directory with default locale files and you can start translating them.
+- The command will create a directory with default locale files.
- After it's done, add the new language to `care_fe/src/Locale/config.ts` file.
-### For improving the existing language
-
-
-
-- Open the Terminal and `cd` to `care_fe/src/Locale`
-- Run the command `node update_locale.js `
- Eg: `node update_locale.js ml` for Malayalam
-- The command will update the new keys which are yet to be translated.
-- You can now start translating or improving it.
-
## Note
-⚠ - If you are adding a new word, then please add it to the Default Locale (EN) first and then proceed with your language.
-
-⚠ - After translating, have a look at its appearance. It may be overflowing or cause some UI breaks. Try to adjust the words such that it fits the UI.
-
⚠ - Try to separate the translation files for each module like `Facility`, `Patient` and more. Don't dump all the keys in one JSON file.
diff --git a/src/Locale/en/Bed.json b/src/Locale/en/Bed.json
index 269658be774..327a4533353 100644
--- a/src/Locale/en/Bed.json
+++ b/src/Locale/en/Bed.json
@@ -9,5 +9,7 @@
"bed_type": "Bed Type",
"make_multiple_beds_label": "Do you want to make multiple beds?",
"number_of_beds": "Number of beds",
- "number_of_beds_out_of_range_error": "Number of beds cannot be greater than 100"
+ "number_of_beds_out_of_range_error": "Number of beds cannot be greater than 100",
+ "bed_created_notification_one": "{{count}} Bed created successfully",
+ "bed_created_notification_other": "{{count}} Beds created successfully"
}
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];
+};