diff --git a/src/common/features.js b/src/common/features.js
new file mode 100644
index 000000000..81df5f3be
--- /dev/null
+++ b/src/common/features.js
@@ -0,0 +1,6 @@
+
+
+export const Features = {
+ Warehouses: 'warehouses',
+ Branches: 'branches'
+}
\ No newline at end of file
diff --git a/src/common/index.js b/src/common/index.js
index 626d9153b..2dc45dc41 100644
--- a/src/common/index.js
+++ b/src/common/index.js
@@ -1,3 +1,4 @@
export * from './TableStyle';
+export * from './features';
export const Align = { Left: 'left', Right: 'right', Center: 'center' };
diff --git a/src/components/FeatureGuard/FeatureCan.js b/src/components/FeatureGuard/FeatureCan.js
new file mode 100644
index 000000000..9e21441b6
--- /dev/null
+++ b/src/components/FeatureGuard/FeatureCan.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import * as R from 'ramda';
+import withFeatureCan from './withFeatureCan';
+
+function FeatureCanJSX({ feature, children, isFeatureCan }) {
+ return isFeatureCan && children;
+}
+
+export const FeatureCan = R.compose(
+ withFeatureCan(({ isFeatureCan }) => ({
+ isFeatureCan,
+ })),
+)(FeatureCanJSX);
diff --git a/src/components/FeatureGuard/index.js b/src/components/FeatureGuard/index.js
new file mode 100644
index 000000000..d8e32e3b0
--- /dev/null
+++ b/src/components/FeatureGuard/index.js
@@ -0,0 +1 @@
+export * from './FeatureCan';
\ No newline at end of file
diff --git a/src/components/FeatureGuard/withFeatureCan.js b/src/components/FeatureGuard/withFeatureCan.js
new file mode 100644
index 000000000..aee7666a8
--- /dev/null
+++ b/src/components/FeatureGuard/withFeatureCan.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux';
+import { getDashboardFeaturesSelector } from '../../store/dashboard/dashboard.selectors';
+
+export default (mapState) => {
+ const featuresSelector = getDashboardFeaturesSelector();
+
+ const mapStateToProps = (state, props) => {
+ const features = featuresSelector(state);
+
+ const mapped = {
+ isFeatureCan: !!features[props.feature],
+ features,
+ };
+ return mapState ? mapState(mapped, state, props) : mapped;
+ };
+ return connect(mapStateToProps);
+};
diff --git a/src/components/index.js b/src/components/index.js
index d24fccb58..79f7b5acd 100644
--- a/src/components/index.js
+++ b/src/components/index.js
@@ -96,6 +96,7 @@ export * from './Skeleton';
export * from './FinancialStatement';
export * from './FinancialReport';
export * from './FinancialSheet';
+export * from './FeatureGuard';
const Hint = FieldHint;
diff --git a/src/containers/Drawers/ItemDetailDrawer/ItemDetailTab.js b/src/containers/Drawers/ItemDetailDrawer/ItemDetailTab.js
index a200ba943..3acaea233 100644
--- a/src/containers/Drawers/ItemDetailDrawer/ItemDetailTab.js
+++ b/src/containers/Drawers/ItemDetailDrawer/ItemDetailTab.js
@@ -1,12 +1,17 @@
import React from 'react';
import { Tab } from '@blueprintjs/core';
+
import { DrawerMainTabs, FormattedMessage as T } from 'components';
import { ItemPaymentTransactions } from './ItemPaymentTransactions';
import ItemDetailHeader from './ItemDetailHeader';
import WarehousesLocationsTable from './WarehousesLocations';
+import { Features } from 'common';
+import { useFeatureCan } from 'hooks/state';
export default function ItemDetailTab() {
+ const { featureCan } = useFeatureCan();
+
return (
}
panel={}
/>
- }
- panel={}
- />
+ {featureCan(Features.Warehouses) && (
+ }
+ panel={}
+ />
+ )}
);
}
diff --git a/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormTopBar.js b/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormTopBar.js
index 1cc828986..90d4c9bbd 100644
--- a/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormTopBar.js
+++ b/src/containers/Sales/Invoices/InvoiceForm/InvoiceFormTopBar.js
@@ -7,48 +7,66 @@ import {
NavbarDivider,
} from '@blueprintjs/core';
-import { Icon, FormattedMessage as T, CustomSelectList } from 'components';
-
+import { useFeatureCan } from 'hooks/state';
+import {
+ Icon,
+ FormattedMessage as T,
+ CustomSelectList,
+ FeatureCan,
+} from 'components';
import { useInvoiceFormContext } from './InvoiceFormProvider';
+import { Features } from 'common';
export default function InvoiceFormTopBar() {
const { warehouses, branches } = useInvoiceFormContext();
+ const { featureCan } = useFeatureCan();
+
+ // Can't display the navigation bar if warehouses or branches feature is not enabled.
+ if (!featureCan(Features.Warehouses) || !featureCan(Features.Branches)) {
+ return null;
+ }
return (
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- {
- form.setFieldValue('branch_id', id);
- }}
- selectedItemId={value}
- buttonProps={{
- icon: ,
- }}
- />
- )}
-
+
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ {
+ form.setFieldValue('branch_id', id);
+ }}
+ selectedItemId={value}
+ buttonProps={{
+ icon: ,
+ }}
+ />
+ )}
+
+
-
-
- {({ form, field: { value }, meta: { error, touched } }) => (
- {
- form.setFieldValue('warehouse_id', id);
- }}
- selectedItemId={value}
- buttonProps={{
- icon: ,
- }}
- />
- )}
-
+ {featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
+
+ )}
+
+
+ {({ form, field: { value }, meta: { error, touched } }) => (
+ {
+ form.setFieldValue('warehouse_id', id);
+ }}
+ selectedItemId={value}
+ buttonProps={{
+ icon: ,
+ }}
+ />
+ )}
+
+
);
diff --git a/src/hooks/state/feature.js b/src/hooks/state/feature.js
new file mode 100644
index 000000000..4ffe96954
--- /dev/null
+++ b/src/hooks/state/feature.js
@@ -0,0 +1,17 @@
+import { useSelector } from 'react-redux';
+import { createSelector } from 'reselect';
+
+const featuresSelector = createSelector(
+ (state) => state.dashboard.features,
+ (features) => features,
+);
+
+export const useFeatureCan = () => {
+ const features = useSelector(featuresSelector);
+
+ return {
+ featureCan: (feature) => {
+ return !!features[feature];
+ },
+ };
+};
diff --git a/src/hooks/state/index.js b/src/hooks/state/index.js
index 75580b504..0b5bb3434 100644
--- a/src/hooks/state/index.js
+++ b/src/hooks/state/index.js
@@ -3,4 +3,5 @@ export * from './authentication';
export * from './globalErrors';
export * from './subscriptions';
export * from './organizations';
-export * from './settings';
\ No newline at end of file
+export * from './settings';
+export * from './feature';
\ No newline at end of file
diff --git a/src/store/dashboard/dashboard.reducer.js b/src/store/dashboard/dashboard.reducer.js
index 4108cf3af..a512de8f8 100644
--- a/src/store/dashboard/dashboard.reducer.js
+++ b/src/store/dashboard/dashboard.reducer.js
@@ -19,6 +19,10 @@ const initialState = {
splashScreenLoading: null,
appIsLoading: true,
appIntlIsLoading: true,
+ features: {
+ branches: true,
+ warehouses: true,
+ },
};
const STORAGE_KEY = 'bigcapital:dashboard';
diff --git a/src/store/dashboard/dashboard.selectors.js b/src/store/dashboard/dashboard.selectors.js
index 53e9f2e5c..fd7ef77d1 100644
--- a/src/store/dashboard/dashboard.selectors.js
+++ b/src/store/dashboard/dashboard.selectors.js
@@ -38,3 +38,12 @@ export const getDrawerPayloadFactory = () =>
createSelector(drawerByNameSelector, (drawer) => {
return { ...drawer?.payload };
});
+
+const featuresSelector = (state, props) => {
+ return state.dashboard.features;
+};
+
+export const getDashboardFeaturesSelector = () =>
+ createSelector(featuresSelector, (features) => {
+ return features;
+ });