mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
feat: add feature guard as hook and component.
This commit is contained in:
6
src/common/features.js
Normal file
6
src/common/features.js
Normal file
@@ -0,0 +1,6 @@
|
||||
|
||||
|
||||
export const Features = {
|
||||
Warehouses: 'warehouses',
|
||||
Branches: 'branches'
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './TableStyle';
|
||||
export * from './features';
|
||||
|
||||
export const Align = { Left: 'left', Right: 'right', Center: 'center' };
|
||||
|
||||
13
src/components/FeatureGuard/FeatureCan.js
Normal file
13
src/components/FeatureGuard/FeatureCan.js
Normal file
@@ -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);
|
||||
1
src/components/FeatureGuard/index.js
Normal file
1
src/components/FeatureGuard/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './FeatureCan';
|
||||
17
src/components/FeatureGuard/withFeatureCan.js
Normal file
17
src/components/FeatureGuard/withFeatureCan.js
Normal file
@@ -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);
|
||||
};
|
||||
@@ -96,6 +96,7 @@ export * from './Skeleton';
|
||||
export * from './FinancialStatement';
|
||||
export * from './FinancialReport';
|
||||
export * from './FinancialSheet';
|
||||
export * from './FeatureGuard';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<DrawerMainTabs renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
@@ -19,11 +24,13 @@ export default function ItemDetailTab() {
|
||||
title={<T id={'transactions'} />}
|
||||
panel={<ItemPaymentTransactions />}
|
||||
/>
|
||||
<Tab
|
||||
id={'warehouses'}
|
||||
title={<T id={'warehouse_locations.label'} />}
|
||||
panel={<WarehousesLocationsTable />}
|
||||
/>
|
||||
{featureCan(Features.Warehouses) && (
|
||||
<Tab
|
||||
id={'warehouses'}
|
||||
title={<T id={'warehouse_locations.label'} />}
|
||||
panel={<WarehousesLocationsTable />}
|
||||
/>
|
||||
)}
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
<Navbar className={'navbar--dashboard-topbar'}>
|
||||
<NavbarGroup align={Alignment.LEFT}>
|
||||
<FastField name={'branch_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<CustomSelectList
|
||||
items={branches}
|
||||
text={'Branch'}
|
||||
onItemSelected={({ id }) => {
|
||||
form.setFieldValue('branch_id', id);
|
||||
}}
|
||||
selectedItemId={value}
|
||||
buttonProps={{
|
||||
icon: <Icon icon={'branch-16'} iconSize={20} />,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
<FeatureCan feature={Features.Warehouses}>
|
||||
<FastField name={'branch_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<CustomSelectList
|
||||
items={branches}
|
||||
text={'Branch'}
|
||||
onItemSelected={({ id }) => {
|
||||
form.setFieldValue('branch_id', id);
|
||||
}}
|
||||
selectedItemId={value}
|
||||
buttonProps={{
|
||||
icon: <Icon icon={'branch-16'} iconSize={20} />,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
</FeatureCan>
|
||||
|
||||
<NavbarDivider />
|
||||
<FastField name={'warehouse_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<CustomSelectList
|
||||
items={warehouses}
|
||||
text={'Warehosue'}
|
||||
onItemSelected={({ id }) => {
|
||||
form.setFieldValue('warehouse_id', id);
|
||||
}}
|
||||
selectedItemId={value}
|
||||
buttonProps={{
|
||||
icon: <Icon icon={'warehouse-16'} iconSize={20} />,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
{featureCan(Features.Warehouses) && featureCan(Features.Branches) && (
|
||||
<NavbarDivider />
|
||||
)}
|
||||
<FeatureCan feature={Features.Warehouses}>
|
||||
<FastField name={'warehouse_id'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
<CustomSelectList
|
||||
items={warehouses}
|
||||
text={'Warehosue'}
|
||||
onItemSelected={({ id }) => {
|
||||
form.setFieldValue('warehouse_id', id);
|
||||
}}
|
||||
selectedItemId={value}
|
||||
buttonProps={{
|
||||
icon: <Icon icon={'warehouse-16'} iconSize={20} />,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</FastField>
|
||||
</FeatureCan>
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
);
|
||||
|
||||
17
src/hooks/state/feature.js
Normal file
17
src/hooks/state/feature.js
Normal file
@@ -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];
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -3,4 +3,5 @@ export * from './authentication';
|
||||
export * from './globalErrors';
|
||||
export * from './subscriptions';
|
||||
export * from './organizations';
|
||||
export * from './settings';
|
||||
export * from './settings';
|
||||
export * from './feature';
|
||||
@@ -19,6 +19,10 @@ const initialState = {
|
||||
splashScreenLoading: null,
|
||||
appIsLoading: true,
|
||||
appIntlIsLoading: true,
|
||||
features: {
|
||||
branches: true,
|
||||
warehouses: true,
|
||||
},
|
||||
};
|
||||
|
||||
const STORAGE_KEY = 'bigcapital:dashboard';
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user