mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-22 15:50:32 +00:00
WIP feature/Bulk delete items
This commit is contained in:
@@ -15,7 +15,7 @@ import { Row, Col } from 'react-grid-system';
|
|||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { Select } from '@blueprintjs/select';
|
import { Select } from '@blueprintjs/select';
|
||||||
import { queryCache } from 'react-query';
|
import { queryCache } from 'react-query';
|
||||||
import {useParams} from 'react-router-dom';
|
import {useParams ,useHistory} from 'react-router-dom';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import ErrorMessage from 'components/ErrorMessage';
|
import ErrorMessage from 'components/ErrorMessage';
|
||||||
@@ -27,13 +27,13 @@ import withAccounts from 'containers/Accounts/withAccounts';
|
|||||||
import withMediaActions from 'containers/Media/withMediaActions';
|
import withMediaActions from 'containers/Media/withMediaActions';
|
||||||
|
|
||||||
import MoneyInputGroup from 'components/MoneyInputGroup';
|
import MoneyInputGroup from 'components/MoneyInputGroup';
|
||||||
import { useHistory } from 'react-router-dom';
|
|
||||||
import Dragzone from 'components/Dragzone';
|
import Dragzone from 'components/Dragzone';
|
||||||
import useMedia from 'hooks/useMedia';
|
import useMedia from 'hooks/useMedia';
|
||||||
import withItems from './withItems';
|
import withItems from './withItems';
|
||||||
import withItemDetail from 'containers/Items/withItemDetail'
|
import withItemDetail from 'containers/Items/withItemDetail'
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
import withDashboardActions from 'containers/Dashboard/withDashboard';
|
||||||
|
import withAccountDetail from 'containers/Accounts/withAccountDetail';
|
||||||
|
|
||||||
|
|
||||||
const ItemForm = ({
|
const ItemForm = ({
|
||||||
@@ -42,11 +42,10 @@ const ItemForm = ({
|
|||||||
requestSubmitItem,
|
requestSubmitItem,
|
||||||
requestEditItem,
|
requestEditItem,
|
||||||
|
|
||||||
|
|
||||||
accounts,
|
accounts,
|
||||||
accountsTypes,
|
|
||||||
itemDetail,
|
itemDetail,
|
||||||
|
onFormSubmit,
|
||||||
|
onCancelForm,
|
||||||
|
|
||||||
|
|
||||||
// #withDashboard
|
// #withDashboard
|
||||||
@@ -65,7 +64,7 @@ const ItemForm = ({
|
|||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const [selectedAccounts, setSelectedAccounts] = useState({});
|
const [selectedAccounts, setSelectedAccounts] = useState({});
|
||||||
const [selectedAccountType, setSelectedAccountType] = useState(null);
|
const [payload, setPayload] = useState({});
|
||||||
|
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
@@ -132,6 +131,9 @@ const ItemForm = ({
|
|||||||
}
|
}
|
||||||
}), [itemDetail, defaultInitialValues]);
|
}), [itemDetail, defaultInitialValues]);
|
||||||
|
|
||||||
|
const saveInvokeSubmit = useCallback((payload) => {
|
||||||
|
onFormSubmit && onFormSubmit(payload)
|
||||||
|
}, [onFormSubmit]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
itemDetail && itemDetail.id ?
|
itemDetail && itemDetail.id ?
|
||||||
@@ -170,6 +172,8 @@ const ItemForm = ({
|
|||||||
intent:Intent.SUCCESS
|
intent:Intent.SUCCESS
|
||||||
});
|
});
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
|
saveInvokeSubmit({action:'update',...payload})
|
||||||
|
history.push('/items');
|
||||||
resetForm();
|
resetForm();
|
||||||
}).catch((errors)=>{
|
}).catch((errors)=>{
|
||||||
setSubmitting(false)
|
setSubmitting(false)
|
||||||
@@ -229,18 +233,6 @@ const ItemForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Set default
|
|
||||||
// useEffect(()=>{
|
|
||||||
|
|
||||||
// if(itemDetail && itemDetail.id){
|
|
||||||
// const defaultType = itemDetail.find(
|
|
||||||
// (t) => t.id === itemDetail.id
|
|
||||||
// );
|
|
||||||
|
|
||||||
// defaultType && setSelectedAccountType(defaultType);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// },[])
|
|
||||||
const onItemAccountSelect = useCallback(
|
const onItemAccountSelect = useCallback(
|
||||||
(filedName) => {
|
(filedName) => {
|
||||||
return (account) => {
|
return (account) => {
|
||||||
@@ -637,10 +629,10 @@ const ItemForm = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
withAccounts(({accounts,accountsTypes})=>({
|
withAccounts(({accounts})=>({
|
||||||
accounts,
|
accounts,
|
||||||
accountsTypes
|
|
||||||
})),
|
})),
|
||||||
|
withAccountDetail,
|
||||||
withItemsActions,
|
withItemsActions,
|
||||||
withItemDetail,
|
withItemDetail,
|
||||||
withItemCategories(({ categoriesList }) => ({
|
withItemCategories(({ categoriesList }) => ({
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { compose } from 'utils';
|
|||||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import FilterDropdown from 'components/FilterDropdown';
|
import FilterDropdown from 'components/FilterDropdown';
|
||||||
import DialogConnect from 'connectors/Dialog.connector';
|
import withDialog from 'connectors/Dialog.connector';
|
||||||
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
import withResourceDetail from 'containers/Resources/withResourceDetails';
|
||||||
import withItems from 'containers/Items/withItems';
|
import withItems from 'containers/Items/withItems';
|
||||||
import { If } from 'components';
|
import { If } from 'components';
|
||||||
@@ -34,6 +34,7 @@ const ItemsActionsBar = ({
|
|||||||
|
|
||||||
onFilterChanged,
|
onFilterChanged,
|
||||||
selectedRows = [],
|
selectedRows = [],
|
||||||
|
onBulkDelete,
|
||||||
}) => {
|
}) => {
|
||||||
const { path } = useRouteMatch();
|
const { path } = useRouteMatch();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
@@ -43,9 +44,11 @@ const ItemsActionsBar = ({
|
|||||||
<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />
|
<MenuItem href={`${path}/${view.id}/custom_view`} text={view.name} />
|
||||||
));
|
));
|
||||||
|
|
||||||
const onClickNewItem = () => {
|
|
||||||
|
const onClickNewItem = useCallback(() => {
|
||||||
history.push('/items/new');
|
history.push('/items/new');
|
||||||
};
|
}, [history]);
|
||||||
|
|
||||||
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
const hasSelectedRows = useMemo(() => selectedRows.length > 0, [
|
||||||
selectedRows,
|
selectedRows,
|
||||||
]);
|
]);
|
||||||
@@ -62,6 +65,11 @@ const ItemsActionsBar = ({
|
|||||||
openDialog('item-form', {});
|
openDialog('item-form', {});
|
||||||
}, [openDialog]);
|
}, [openDialog]);
|
||||||
|
|
||||||
|
const handleBulkDelete = useCallback(() => {
|
||||||
|
onBulkDelete && onBulkDelete(selectedRows.map(r => r.id));
|
||||||
|
}, [onBulkDelete, selectedRows]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardActionsBar>
|
<DashboardActionsBar>
|
||||||
<NavbarGroup>
|
<NavbarGroup>
|
||||||
@@ -116,9 +124,10 @@ const ItemsActionsBar = ({
|
|||||||
<If condition={hasSelectedRows}>
|
<If condition={hasSelectedRows}>
|
||||||
<Button
|
<Button
|
||||||
className={Classes.MINIMAL}
|
className={Classes.MINIMAL}
|
||||||
|
icon={<Icon icon='trash' iconSize={15} />}
|
||||||
|
text={<T id={'delete'}/>}
|
||||||
intent={Intent.DANGER}
|
intent={Intent.DANGER}
|
||||||
icon={<Icon icon='trash' />}
|
onClick={handleBulkDelete}
|
||||||
text={<T id={'delete'} />}
|
|
||||||
/>
|
/>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
@@ -138,7 +147,7 @@ const ItemsActionsBar = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
DialogConnect,
|
withDialog,
|
||||||
withItems(({ itemsViews }) => ({
|
withItems(({ itemsViews }) => ({
|
||||||
itemsViews,
|
itemsViews,
|
||||||
})),
|
})),
|
||||||
|
|||||||
@@ -137,6 +137,9 @@ const ItemsDataTable = ({
|
|||||||
onFetchData={handleFetchData}
|
onFetchData={handleFetchData}
|
||||||
loading={itemsTableLoading && !initialMount}
|
loading={itemsTableLoading && !initialMount}
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
|
expandable={true}
|
||||||
|
treeGraph={true}
|
||||||
|
spinnerProps={{size: 30}}
|
||||||
onSelectedRowsChange={handleSelectedRowsChange} />
|
onSelectedRowsChange={handleSelectedRowsChange} />
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useCallback, useState } from 'react';
|
import React, { useEffect, useCallback, useState,useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Route,
|
Route,
|
||||||
Switch,
|
Switch,
|
||||||
@@ -43,10 +43,12 @@ function ItemsList({
|
|||||||
requestDeleteItem,
|
requestDeleteItem,
|
||||||
requestFetchItems,
|
requestFetchItems,
|
||||||
addItemsTableQueries,
|
addItemsTableQueries,
|
||||||
|
requestDeleteBulkItems,
|
||||||
}) {
|
}) {
|
||||||
const [deleteItem, setDeleteItem] = useState(false);
|
const [deleteItem, setDeleteItem] = useState(false);
|
||||||
const [selectedRows, setSelectedRows] = useState([]);
|
const [selectedRows, setSelectedRows] = useState([]);
|
||||||
const [tableLoading, setTableLoading] = useState(false);
|
const [tableLoading, setTableLoading] = useState(false);
|
||||||
|
const [bulkDelete, setBulkDelete] = useState(false);
|
||||||
|
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
@@ -131,17 +133,45 @@ function ItemsList({
|
|||||||
setSelectedRows(accounts);
|
setSelectedRows(accounts);
|
||||||
}, [setSelectedRows]);
|
}, [setSelectedRows]);
|
||||||
|
|
||||||
|
// Calculates the data table selected rows count.
|
||||||
|
const selectedRowsCount = useMemo(() => Object.values(selectedRows).length, [selectedRows]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Handle items bulk delete button click.,
|
||||||
|
|
||||||
|
const handleBulkDelete = useCallback((itemsIds) => {
|
||||||
|
setBulkDelete(itemsIds);
|
||||||
|
}, [setBulkDelete]);
|
||||||
|
|
||||||
|
// Handle confirm items bulk delete.
|
||||||
|
const handleConfirmBulkDelete = useCallback(() => {
|
||||||
|
requestDeleteBulkItems(bulkDelete).then(() => {
|
||||||
|
setBulkDelete(false);
|
||||||
|
AppToaster.show({
|
||||||
|
message: formatMessage({ id: 'the_items_has_been_successfully_deleted' }),
|
||||||
|
intent: Intent.SUCCESS,
|
||||||
|
});
|
||||||
|
}).catch((errors) => {
|
||||||
|
setBulkDelete(false);
|
||||||
|
});
|
||||||
|
}, [requestDeleteBulkItems, bulkDelete]);
|
||||||
|
|
||||||
|
// Handle cancel accounts bulk delete.
|
||||||
|
const handleCancelBulkDelete = useCallback(() => {
|
||||||
|
setBulkDelete(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DashboardInsider
|
<DashboardInsider
|
||||||
isLoading={fetchHook.isFetching}
|
isLoading={fetchHook.isFetching}
|
||||||
name={'items-list'}>
|
name={'items-list'}>
|
||||||
|
|
||||||
<ItemsActionsBar
|
<ItemsActionsBar
|
||||||
|
selectedRows={selectedRows}
|
||||||
onFilterChanged={handleFilterChanged}
|
onFilterChanged={handleFilterChanged}
|
||||||
selectedRows={selectedRows} />
|
onBulkDelete={handleBulkDelete}/>
|
||||||
|
|
||||||
<DashboardPageContent>
|
<DashboardPageContent>
|
||||||
<Switch>
|
<Switch>
|
||||||
@@ -174,6 +204,20 @@ function ItemsList({
|
|||||||
id={'once_delete_this_item_you_will_able_to_restore_it'} />
|
id={'once_delete_this_item_you_will_able_to_restore_it'} />
|
||||||
</p>
|
</p>
|
||||||
</Alert>
|
</Alert>
|
||||||
|
|
||||||
|
<Alert
|
||||||
|
cancelButtonText={<T id={'cancel'}/>}
|
||||||
|
confirmButtonText={`${formatMessage({id:'delete'})} (${selectedRowsCount})`}
|
||||||
|
icon="trash"
|
||||||
|
intent={Intent.DANGER}
|
||||||
|
isOpen={bulkDelete}
|
||||||
|
onCancel={handleCancelBulkDelete}
|
||||||
|
onConfirm={handleConfirmBulkDelete}
|
||||||
|
>
|
||||||
|
<p>
|
||||||
|
<T id={'once_delete_these_items_you_will_not_able_restore_them'} />
|
||||||
|
</p>
|
||||||
|
</Alert>
|
||||||
</Route>
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</DashboardPageContent>
|
</DashboardPageContent>
|
||||||
|
|||||||
@@ -4,12 +4,14 @@ import {
|
|||||||
deleteItem,
|
deleteItem,
|
||||||
submitItem,
|
submitItem,
|
||||||
editItem,
|
editItem,
|
||||||
|
deleteBulkItems
|
||||||
} from 'store/items/items.actions';
|
} from 'store/items/items.actions';
|
||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
export const mapDispatchToProps = (dispatch) => ({
|
export const mapDispatchToProps = (dispatch) => ({
|
||||||
requestFetchItems: (query) => dispatch(fetchItems({ query })),
|
requestFetchItems: (query) => dispatch(fetchItems({ query })),
|
||||||
requestDeleteItem: (id) => dispatch(deleteItem({ id })),
|
requestDeleteItem: (id) => dispatch(deleteItem({ id })),
|
||||||
|
requestDeleteBulkItems:(ids)=>dispatch(deleteBulkItems({ids})),
|
||||||
requestSubmitItem: (form) => dispatch(submitItem({ form })),
|
requestSubmitItem: (form) => dispatch(submitItem({ form })),
|
||||||
requestEditItem:(id,form) => dispatch(editItem({id,form})),
|
requestEditItem:(id,form) => dispatch(editItem({id,form})),
|
||||||
addBulkActionItem: (id) => dispatch({
|
addBulkActionItem: (id) => dispatch({
|
||||||
|
|||||||
@@ -322,7 +322,10 @@ export default {
|
|||||||
organization_industry_:'Organization industry',
|
organization_industry_:'Organization industry',
|
||||||
base_currency_:'Base currency',
|
base_currency_:'Base currency',
|
||||||
date_format_:'Date format',
|
date_format_:'Date format',
|
||||||
view_name_:'View name'
|
category_name_:'Category name',
|
||||||
|
view_name_:'View name',
|
||||||
|
the_items_has_been_successfully_deleted: 'The items have been successfully deleted.',
|
||||||
|
once_delete_these_items_you_will_not_able_restore_them: 'Once you delete these items, you won\'t be able to retrieve them later. Are you sure you want to delete them?',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -76,3 +76,19 @@ export const deleteItem = ({ id }) => {
|
|||||||
}).catch((error) => { reject(error); });
|
}).catch((error) => { reject(error); });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const deleteBulkItems = ({ ids }) => {
|
||||||
|
return dispatch => new Promise((resolve, reject) => {
|
||||||
|
ApiService.delete(`items`, { params: { ids }}).then((response) => {
|
||||||
|
dispatch({
|
||||||
|
type: t.ITEMS_BULK_DELETE,
|
||||||
|
payload: { ids }
|
||||||
|
});
|
||||||
|
resolve(response);
|
||||||
|
}).catch((error) => {
|
||||||
|
reject(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
@@ -12,4 +12,6 @@ export default {
|
|||||||
|
|
||||||
ITEMS_TABLE_LOADING: 'ITEMS_TABLE_LOADING',
|
ITEMS_TABLE_LOADING: 'ITEMS_TABLE_LOADING',
|
||||||
ITEMS_SET_CURRENT_VIEW: 'ITEMS_SET_CURRENT_VIEW',
|
ITEMS_SET_CURRENT_VIEW: 'ITEMS_SET_CURRENT_VIEW',
|
||||||
|
ITEMS_BULK_DELETE:'ITEMS_BULK_DELETE'
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user