mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
feat: item & inventory.
This commit is contained in:
17
src/common/abilityOption.js
Normal file
17
src/common/abilityOption.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
export const AbilitySubject = {
|
||||||
|
Item: 'Item',
|
||||||
|
InventoryAdjustment: 'InventoryAdjustment',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ItemAbility = {
|
||||||
|
View: 'view',
|
||||||
|
Create: 'create',
|
||||||
|
Edit: 'edit',
|
||||||
|
Delete: 'delete',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InventoryAdjustment = {
|
||||||
|
View: 'view',
|
||||||
|
Create: 'create',
|
||||||
|
Delete: 'delete',
|
||||||
|
};
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { AbilityBuilder, defineAbility } from '@casl/ability';
|
|
||||||
import { createContextualCan } from '@casl/react';
|
|
||||||
import { createContext } from 'react';
|
|
||||||
|
|
||||||
export const AbilityContext = createContext();
|
|
||||||
export const Can = createContextualCan(AbilityContext.Consumer);
|
|
||||||
|
|
||||||
export const ability = defineAbility((can, cannot) => {
|
|
||||||
cannot('Item', 'create');
|
|
||||||
});
|
|
||||||
@@ -1,4 +1,29 @@
|
|||||||
import { createCanBoundTo } from '@casl/react';
|
import React from 'react';
|
||||||
import ability from '../components/Config/ability';
|
import { Ability } from '@casl/ability';
|
||||||
|
import { createContextualCan } from '@casl/react';
|
||||||
|
|
||||||
export default createCanBoundTo(ability);
|
import {
|
||||||
|
ItemAbility,
|
||||||
|
AbilitySubject,
|
||||||
|
InventoryAdjustment,
|
||||||
|
} from '../common/abilityOption';
|
||||||
|
|
||||||
|
export const AbilityContext = React.createContext();
|
||||||
|
export const Can = createContextualCan(AbilityContext.Consumer);
|
||||||
|
|
||||||
|
const AbilityContextProvider = (props) => {
|
||||||
|
const ability = new Ability([
|
||||||
|
{
|
||||||
|
subject: [AbilitySubject.Item],
|
||||||
|
action: [ItemAbility.Create, ItemAbility.Edit],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AbilityContext.Provider value={ability}>
|
||||||
|
{props.children}
|
||||||
|
</AbilityContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AbilityContextProvider;
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
import { AbilityBuilder } from '@casl/ability';
|
|
||||||
// import { AbilitySubject, ItemAbility } from '../../common/abilityOption';
|
|
||||||
|
|
||||||
export function defineAbilitiesFor(role) {
|
|
||||||
const { rules, can } = new AbilityBuilder();
|
|
||||||
|
|
||||||
can('create', 'Item');
|
|
||||||
|
|
||||||
return new Ability(rules);
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import AbilityContextProvider from '../../components/Can';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dashboard provider.
|
* Dashboard provider.
|
||||||
*/
|
*/
|
||||||
export default function DashboardProvider({ children }) {
|
export default function DashboardProvider({ children }) {
|
||||||
return children;
|
return <AbilityContextProvider>{children}</AbilityContextProvider>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ import AvaterCell from './AvaterCell';
|
|||||||
|
|
||||||
import { ItemsMultiSelect } from './Items';
|
import { ItemsMultiSelect } from './Items';
|
||||||
import MoreMenuItems from './MoreMenutItems';
|
import MoreMenuItems from './MoreMenutItems';
|
||||||
|
import { Can } from './Can';
|
||||||
|
|
||||||
export * from './Dialog';
|
export * from './Dialog';
|
||||||
export * from './Menu';
|
export * from './Menu';
|
||||||
@@ -156,4 +157,5 @@ export {
|
|||||||
Card,
|
Card,
|
||||||
AvaterCell,
|
AvaterCell,
|
||||||
MoreMenuItems,
|
MoreMenuItems,
|
||||||
|
Can,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,11 @@ import { useInventoryAdjustmentDrawerContext } from './InventoryAdjustmentDrawer
|
|||||||
|
|
||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
|
|
||||||
import { Icon, FormattedMessage as T } from 'components';
|
import { Icon, FormattedMessage as T, Can } from 'components';
|
||||||
|
import {
|
||||||
|
InventoryAdjustment,
|
||||||
|
AbilitySubject,
|
||||||
|
} from '../../../common/abilityOption';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
@@ -26,6 +30,7 @@ function InventoryAdjustmentDetailActionsBar({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<Can I={InventoryAdjustment.Delete} a={AbilitySubject.InventoryAdjustment}>
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
<Button
|
<Button
|
||||||
@@ -37,6 +42,7 @@ function InventoryAdjustmentDetailActionsBar({
|
|||||||
/>
|
/>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DashboardActionsBar>
|
||||||
|
</Can>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ import {
|
|||||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
|
|
||||||
import { useItemDetailDrawerContext } from './ItemDetailDrawerProvider';
|
import { useItemDetailDrawerContext } from './ItemDetailDrawerProvider';
|
||||||
|
import { ItemAbility, AbilitySubject } from '../../../common/abilityOption';
|
||||||
|
|
||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||||
|
|
||||||
import { Icon, FormattedMessage as T } from 'components';
|
import { Icon, FormattedMessage as T, Can } from 'components';
|
||||||
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
|
|
||||||
@@ -47,13 +48,17 @@ function ItemDetailActionsBar({
|
|||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
|
<Can I={ItemAbility.Edit} a={AbilitySubject.Item}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon="pen-18" />}
|
icon={<Icon icon="pen-18" />}
|
||||||
text={<T id={'edit_item'} />}
|
text={<T id={'edit_item'} />}
|
||||||
onClick={handleEditItem}
|
onClick={handleEditItem}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
|
</Can>
|
||||||
|
<Can I={ItemAbility.Delete} a={AbilitySubject.Item}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||||
@@ -61,6 +66,7 @@ function ItemDetailActionsBar({
|
|||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
onClick={handleDeleteItem}
|
onClick={handleDeleteItem}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
</DashboardActionsBar>
|
</DashboardActionsBar>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -12,10 +12,14 @@ import {
|
|||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import { FormattedMessage as T } from 'components';
|
import { FormattedMessage as T, Can } from 'components';
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
import { Icon, Money, If } from 'components';
|
import { Icon, Money, If } from 'components';
|
||||||
import { isBlank, safeCallback } from 'utils';
|
import { isBlank, safeCallback } from 'utils';
|
||||||
|
import {
|
||||||
|
InventoryAdjustment,
|
||||||
|
AbilitySubject,
|
||||||
|
} from '../../common/abilityOption';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish accessor
|
* Publish accessor
|
||||||
@@ -102,20 +106,30 @@ export const ActionsMenu = ({
|
|||||||
text={intl.get('view_details')}
|
text={intl.get('view_details')}
|
||||||
onClick={safeCallback(onViewDetails, original)}
|
onClick={safeCallback(onViewDetails, original)}
|
||||||
/>
|
/>
|
||||||
<MenuDivider />
|
|
||||||
<If condition={!original.is_published}>
|
<If condition={!original.is_published}>
|
||||||
|
<MenuDivider />
|
||||||
|
<Can
|
||||||
|
I={InventoryAdjustment.Create}
|
||||||
|
a={AbilitySubject.InventoryAdjustment}
|
||||||
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon={'arrow-to-top'} size={16} />}
|
icon={<Icon icon={'arrow-to-top'} size={16} />}
|
||||||
text={intl.get('publish_adjustment')}
|
text={intl.get('publish_adjustment')}
|
||||||
onClick={safeCallback(onPublish, original)}
|
onClick={safeCallback(onPublish, original)}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</If>
|
</If>
|
||||||
|
<Can
|
||||||
|
I={InventoryAdjustment.Delete}
|
||||||
|
a={AbilitySubject.InventoryAdjustment}
|
||||||
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={intl.get('delete_adjustment')}
|
text={intl.get('delete_adjustment')}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
onClick={safeCallback(onDelete, original)}
|
onClick={safeCallback(onDelete, original)}
|
||||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
|||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import {
|
import {
|
||||||
If,
|
If,
|
||||||
|
Can,
|
||||||
DashboardActionViewsList,
|
DashboardActionViewsList,
|
||||||
AdvancedFilterPopover,
|
AdvancedFilterPopover,
|
||||||
DashboardFilterButton,
|
DashboardFilterButton,
|
||||||
@@ -30,8 +31,7 @@ import withSettings from '../Settings/withSettings';
|
|||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import withSettingsActions from '../Settings/withSettingsActions';
|
import withSettingsActions from '../Settings/withSettingsActions';
|
||||||
|
|
||||||
|
import { ItemAbility, AbilitySubject } from '../../common/abilityOption';
|
||||||
import { Can, AbilityContext } from '../../components/Abilities';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Items actions bar.
|
* Items actions bar.
|
||||||
@@ -60,8 +60,6 @@ function ItemsActionsBar({
|
|||||||
// Items refresh action.
|
// Items refresh action.
|
||||||
const { refresh } = useRefreshItems();
|
const { refresh } = useRefreshItems();
|
||||||
|
|
||||||
const { ability } = React.useContext(AbilityContext);
|
|
||||||
|
|
||||||
// History context.
|
// History context.
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
@@ -106,14 +104,14 @@ function ItemsActionsBar({
|
|||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
|
|
||||||
{/* <Can I="create" a="Item" ability={ability}> */}
|
<Can I={ItemAbility.Create} a={AbilitySubject.Item}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
icon={<Icon icon="plus" />}
|
icon={<Icon icon="plus" />}
|
||||||
text={<T id={'new_item'} />}
|
text={<T id={'new_item'} />}
|
||||||
onClick={onClickNewItem}
|
onClick={onClickNewItem}
|
||||||
/>
|
/>
|
||||||
{/* </Can> */}
|
</Can>
|
||||||
<AdvancedFilterPopover
|
<AdvancedFilterPopover
|
||||||
advancedFilterProps={{
|
advancedFilterProps={{
|
||||||
conditions: itemsFilterRoles,
|
conditions: itemsFilterRoles,
|
||||||
@@ -155,11 +153,13 @@ function ItemsActionsBar({
|
|||||||
onChange={handleTableRowSizeChange}
|
onChange={handleTableRowSizeChange}
|
||||||
/>
|
/>
|
||||||
<NavbarDivider />
|
<NavbarDivider />
|
||||||
|
<Can I={ItemAbility.View} a={AbilitySubject.Item}>
|
||||||
<Switch
|
<Switch
|
||||||
labelElement={<T id={'inactive'} />}
|
labelElement={<T id={'inactive'} />}
|
||||||
defaultChecked={itemsInactiveMode}
|
defaultChecked={itemsInactiveMode}
|
||||||
onChange={handleInactiveSwitchChange}
|
onChange={handleInactiveSwitchChange}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</NavbarGroup>
|
</NavbarGroup>
|
||||||
|
|
||||||
<NavbarGroup align={Alignment.RIGHT}>
|
<NavbarGroup align={Alignment.RIGHT}>
|
||||||
|
|||||||
@@ -12,8 +12,13 @@ import {
|
|||||||
import intl from 'react-intl-universal';
|
import intl from 'react-intl-universal';
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
|
|
||||||
import { FormattedMessage as T, Icon, Money, If } from 'components';
|
import { FormattedMessage as T, Icon, Money, If, Can } from 'components';
|
||||||
import { isBlank, safeCallback } from 'utils';
|
import { isBlank, safeCallback } from 'utils';
|
||||||
|
import {
|
||||||
|
ItemAbility,
|
||||||
|
InventoryAdjustment,
|
||||||
|
AbilitySubject,
|
||||||
|
} from '../../common/abilityOption';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Publish accessor
|
* Publish accessor
|
||||||
@@ -91,43 +96,58 @@ export function ItemsActionMenuList({
|
|||||||
onClick={safeCallback(onViewDetails, original)}
|
onClick={safeCallback(onViewDetails, original)}
|
||||||
/>
|
/>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
|
<Can I={ItemAbility.Edit} a={AbilitySubject.Item}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon="pen-18" />}
|
icon={<Icon icon="pen-18" />}
|
||||||
text={intl.get('edit_item')}
|
text={intl.get('edit_item')}
|
||||||
onClick={safeCallback(onEditItem, original)}
|
onClick={safeCallback(onEditItem, original)}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
|
<Can I={ItemAbility.Create} a={AbilitySubject.Item}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon="duplicate-16" />}
|
icon={<Icon icon="duplicate-16" />}
|
||||||
text={intl.get('duplicate')}
|
text={intl.get('duplicate')}
|
||||||
onClick={safeCallback(onDuplicate, original)}
|
onClick={safeCallback(onDuplicate, original)}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
<If condition={original.active}>
|
<If condition={original.active}>
|
||||||
|
<Can I={ItemAbility.View} a={AbilitySubject.Item}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={intl.get('inactivate_item')}
|
text={intl.get('inactivate_item')}
|
||||||
icon={<Icon icon="pause-16" iconSize={16} />}
|
icon={<Icon icon="pause-16" iconSize={16} />}
|
||||||
onClick={safeCallback(onInactivateItem, original)}
|
onClick={safeCallback(onInactivateItem, original)}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</If>
|
</If>
|
||||||
<If condition={!original.active}>
|
<If condition={!original.active}>
|
||||||
|
<Can I={ItemAbility.View} a={AbilitySubject.Item}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={intl.get('activate_item')}
|
text={intl.get('activate_item')}
|
||||||
icon={<Icon icon="play-16" iconSize={16} />}
|
icon={<Icon icon="play-16" iconSize={16} />}
|
||||||
onClick={safeCallback(onActivateItem, original)}
|
onClick={safeCallback(onActivateItem, original)}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</If>
|
</If>
|
||||||
<If condition={original.type === 'inventory'}>
|
<If condition={original.type === 'inventory'}>
|
||||||
|
<Can
|
||||||
|
I={InventoryAdjustment.Create}
|
||||||
|
a={AbilitySubject.InventoryAdjustment}
|
||||||
|
>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={intl.get('make_adjustment')}
|
text={intl.get('make_adjustment')}
|
||||||
icon={<Icon icon={'swap-vert'} iconSize={16} />}
|
icon={<Icon icon={'swap-vert'} iconSize={16} />}
|
||||||
onClick={safeCallback(onMakeAdjustment, original)}
|
onClick={safeCallback(onMakeAdjustment, original)}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</If>
|
</If>
|
||||||
|
<Can I={ItemAbility.Delete} a={AbilitySubject.Item}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={intl.get('delete_item')}
|
text={intl.get('delete_item')}
|
||||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||||
onClick={safeCallback(onDeleteItem, original)}
|
onClick={safeCallback(onDeleteItem, original)}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
/>
|
/>
|
||||||
|
</Can>
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user