mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
Merge branch 'feature/item-duplicate'
This commit is contained in:
@@ -60,14 +60,14 @@ function ItemForm({
|
|||||||
createItemMutate,
|
createItemMutate,
|
||||||
editItemMutate,
|
editItemMutate,
|
||||||
submitPayload,
|
submitPayload,
|
||||||
isNewMode
|
isNewMode,
|
||||||
} = useItemFormContext();
|
} = useItemFormContext();
|
||||||
|
|
||||||
// History context.
|
// History context.
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial values in create and edit mode.
|
* Initial values in create and edit mode.
|
||||||
*/
|
*/
|
||||||
@@ -97,7 +97,11 @@ function ItemForm({
|
|||||||
|
|
||||||
// Transform API errors.
|
// Transform API errors.
|
||||||
const transformApiErrors = (error) => {
|
const transformApiErrors = (error) => {
|
||||||
const { response: { data: { errors } } } = error;
|
const {
|
||||||
|
response: {
|
||||||
|
data: { errors },
|
||||||
|
},
|
||||||
|
} = error;
|
||||||
const fields = {};
|
const fields = {};
|
||||||
|
|
||||||
if (errors.find((e) => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
|
if (errors.find((e) => e.type === 'ITEM.NAME.ALREADY.EXISTS')) {
|
||||||
@@ -118,9 +122,10 @@ function ItemForm({
|
|||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage(
|
message: formatMessage(
|
||||||
{
|
{
|
||||||
id: isNewMode
|
id:
|
||||||
? 'the_item_has_been_created_successfully'
|
isNewMode
|
||||||
: 'the_item_has_been_edited_successfully',
|
? 'the_item_has_been_created_successfully'
|
||||||
|
: 'the_item_has_been_edited_successfully',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
number: itemId,
|
number: itemId,
|
||||||
@@ -151,7 +156,7 @@ function ItemForm({
|
|||||||
editItemMutate([itemId, form]).then(onSuccess).catch(onError);
|
editItemMutate([itemId, form]).then(onSuccess).catch(onError);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_ITEM)}>
|
<div class={classNames(CLASSES.PAGE_FORM_ITEM)}>
|
||||||
<Formik
|
<Formik
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, createContext, useState } from 'react';
|
import React, { useEffect, createContext, useState } from 'react';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
|
import { useLocation, useParams } from 'react-router-dom';
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import {
|
import {
|
||||||
useItem,
|
useItem,
|
||||||
@@ -16,6 +17,9 @@ const ItemFormContext = createContext();
|
|||||||
* Accounts chart data provider.
|
* Accounts chart data provider.
|
||||||
*/
|
*/
|
||||||
function ItemFormProvider({ itemId, ...props }) {
|
function ItemFormProvider({ itemId, ...props }) {
|
||||||
|
const { state } = useLocation();
|
||||||
|
|
||||||
|
const duplicateId = state?.action;
|
||||||
// Fetches the accounts list.
|
// Fetches the accounts list.
|
||||||
const { isFetching: isAccountsLoading, data: accounts } = useAccounts();
|
const { isFetching: isAccountsLoading, data: accounts } = useAccounts();
|
||||||
|
|
||||||
@@ -26,9 +30,12 @@ function ItemFormProvider({ itemId, ...props }) {
|
|||||||
} = useItemsCategories();
|
} = useItemsCategories();
|
||||||
|
|
||||||
// Fetches the given item details.
|
// Fetches the given item details.
|
||||||
const { isFetching: isItemLoading, data: item } = useItem(itemId, {
|
const { isFetching: isItemLoading, data: item } = useItem(
|
||||||
enabled: !!itemId,
|
itemId || duplicateId,
|
||||||
});
|
{
|
||||||
|
enabled: !!itemId || !!duplicateId,
|
||||||
|
},
|
||||||
|
);
|
||||||
// Create and edit item mutations.
|
// Create and edit item mutations.
|
||||||
const { mutateAsync: editItemMutate } = useEditItem();
|
const { mutateAsync: editItemMutate } = useEditItem();
|
||||||
const { mutateAsync: createItemMutate } = useCreateItem();
|
const { mutateAsync: createItemMutate } = useCreateItem();
|
||||||
@@ -37,7 +44,7 @@ function ItemFormProvider({ itemId, ...props }) {
|
|||||||
const [submitPayload, setSubmitPayload] = useState({});
|
const [submitPayload, setSubmitPayload] = useState({});
|
||||||
|
|
||||||
// Detarmines whether the form new mode.
|
// Detarmines whether the form new mode.
|
||||||
const isNewMode = !itemId;
|
const isNewMode = duplicateId || !itemId;
|
||||||
|
|
||||||
// Provider state.
|
// Provider state.
|
||||||
const provider = {
|
const provider = {
|
||||||
@@ -54,20 +61,20 @@ function ItemFormProvider({ itemId, ...props }) {
|
|||||||
|
|
||||||
createItemMutate,
|
createItemMutate,
|
||||||
editItemMutate,
|
editItemMutate,
|
||||||
setSubmitPayload
|
setSubmitPayload,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format message intl.
|
// Format message intl.
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
// Change page title dispatcher.
|
// Change page title dispatcher.
|
||||||
const changePageTitle = useDashboardPageTitle();
|
const changePageTitle = useDashboardPageTitle();
|
||||||
|
|
||||||
// Changes the page title in new and edit mode.
|
// Changes the page title in new and edit mode.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
!isNewMode
|
isNewMode
|
||||||
? changePageTitle(formatMessage({ id: 'edit_item_details' }))
|
? changePageTitle(formatMessage({ id: 'new_item' }))
|
||||||
: changePageTitle(formatMessage({ id: 'new_item' }));
|
: changePageTitle(formatMessage({ id: 'edit_item_details' }));
|
||||||
}, [changePageTitle, isNewMode, formatMessage]);
|
}, [changePageTitle, isNewMode, formatMessage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import ItemsEmptyStatus from './ItemsEmptyStatus';
|
|||||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||||
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||||
|
|
||||||
|
|
||||||
import withItems from 'containers/Items/withItems';
|
import withItems from 'containers/Items/withItems';
|
||||||
import withItemsActions from 'containers/Items/withItemsActions';
|
import withItemsActions from 'containers/Items/withItemsActions';
|
||||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||||
@@ -94,6 +93,11 @@ function ItemsDataTable({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Display empty status instead of the table.
|
// Display empty status instead of the table.
|
||||||
|
const handleDuplicate = ({ id }) => {
|
||||||
|
history.push(`/items/new?duplicate=${id}`, { action: id });
|
||||||
|
};
|
||||||
|
|
||||||
|
// Cannot continue in case the items has empty status.
|
||||||
if (isEmptyStatus) {
|
if (isEmptyStatus) {
|
||||||
return <ItemsEmptyStatus />;
|
return <ItemsEmptyStatus />;
|
||||||
}
|
}
|
||||||
@@ -103,29 +107,23 @@ function ItemsDataTable({
|
|||||||
columns={columns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
initialState={itemsTableState}
|
initialState={itemsTableState}
|
||||||
|
|
||||||
loading={isItemsLoading}
|
loading={isItemsLoading}
|
||||||
headerLoading={isItemsLoading}
|
headerLoading={isItemsLoading}
|
||||||
progressBarLoading={isItemsFetching}
|
progressBarLoading={isItemsFetching}
|
||||||
|
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
selectionColumn={true}
|
selectionColumn={true}
|
||||||
spinnerProps={{ size: 30 }}
|
spinnerProps={{ size: 30 }}
|
||||||
expandable={false}
|
expandable={false}
|
||||||
sticky={true}
|
sticky={true}
|
||||||
rowClassNames={rowClassNames}
|
rowClassNames={rowClassNames}
|
||||||
|
|
||||||
pagination={true}
|
pagination={true}
|
||||||
manualSortBy={true}
|
manualSortBy={true}
|
||||||
manualPagination={true}
|
manualPagination={true}
|
||||||
pagesCount={pagination.pagesCount}
|
pagesCount={pagination.pagesCount}
|
||||||
|
|
||||||
autoResetSortBy={false}
|
autoResetSortBy={false}
|
||||||
autoResetPage={true}
|
autoResetPage={true}
|
||||||
|
|
||||||
TableLoadingRenderer={TableSkeletonRows}
|
TableLoadingRenderer={TableSkeletonRows}
|
||||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||||
|
|
||||||
ContextMenu={ItemsActionMenuList}
|
ContextMenu={ItemsActionMenuList}
|
||||||
onFetchData={handleFetchData}
|
onFetchData={handleFetchData}
|
||||||
payload={{
|
payload={{
|
||||||
@@ -134,6 +132,7 @@ function ItemsDataTable({
|
|||||||
onInactivateItem: handleInactiveItem,
|
onInactivateItem: handleInactiveItem,
|
||||||
onActivateItem: handleActivateItem,
|
onActivateItem: handleActivateItem,
|
||||||
onMakeAdjustment: handleMakeAdjustment,
|
onMakeAdjustment: handleMakeAdjustment,
|
||||||
|
onDuplicate: handleDuplicate,
|
||||||
}}
|
}}
|
||||||
noResults={'There is no items in the table yet.'}
|
noResults={'There is no items in the table yet.'}
|
||||||
{...tableProps}
|
{...tableProps}
|
||||||
|
|||||||
@@ -79,10 +79,10 @@ export function ItemsActionMenuList({
|
|||||||
onActivateItem,
|
onActivateItem,
|
||||||
onMakeAdjustment,
|
onMakeAdjustment,
|
||||||
onDeleteItem,
|
onDeleteItem,
|
||||||
|
onDuplicate,
|
||||||
},
|
},
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@@ -95,6 +95,11 @@ export function ItemsActionMenuList({
|
|||||||
text={formatMessage({ id: 'edit_item' })}
|
text={formatMessage({ id: 'edit_item' })}
|
||||||
onClick={safeCallback(onEditItem, original)}
|
onClick={safeCallback(onEditItem, original)}
|
||||||
/>
|
/>
|
||||||
|
<MenuItem
|
||||||
|
icon={<Icon icon="duplicate-18" />}
|
||||||
|
text={formatMessage({ id: 'duplicate' })}
|
||||||
|
onClick={safeCallback(onDuplicate, original)}
|
||||||
|
/>
|
||||||
<If condition={original.active}>
|
<If condition={original.active}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
text={formatMessage({ id: 'inactivate_item' })}
|
text={formatMessage({ id: 'inactivate_item' })}
|
||||||
@@ -153,7 +158,7 @@ export const useItemsTableColumns = () => {
|
|||||||
width: 180,
|
width: 180,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id:'code',
|
id: 'code',
|
||||||
Header: formatMessage({ id: 'item_code' }),
|
Header: formatMessage({ id: 'item_code' }),
|
||||||
accessor: 'code',
|
accessor: 'code',
|
||||||
className: 'code',
|
className: 'code',
|
||||||
@@ -174,7 +179,7 @@ export const useItemsTableColumns = () => {
|
|||||||
width: 150,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id:'sell_price',
|
id: 'sell_price',
|
||||||
Header: formatMessage({ id: 'sell_price' }),
|
Header: formatMessage({ id: 'sell_price' }),
|
||||||
Cell: SellPriceCell,
|
Cell: SellPriceCell,
|
||||||
accessor: 'sell_price',
|
accessor: 'sell_price',
|
||||||
@@ -182,7 +187,7 @@ export const useItemsTableColumns = () => {
|
|||||||
width: 150,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id:'cost_price',
|
id: 'cost_price',
|
||||||
Header: formatMessage({ id: 'cost_price' }),
|
Header: formatMessage({ id: 'cost_price' }),
|
||||||
Cell: CostPriceCell,
|
Cell: CostPriceCell,
|
||||||
accessor: 'cost_price',
|
accessor: 'cost_price',
|
||||||
@@ -190,7 +195,7 @@ export const useItemsTableColumns = () => {
|
|||||||
width: 150,
|
width: 150,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id:'quantity_on_hand',
|
id: 'quantity_on_hand',
|
||||||
Header: formatMessage({ id: 'quantity_on_hand' }),
|
Header: formatMessage({ id: 'quantity_on_hand' }),
|
||||||
accessor: 'quantity_on_hand',
|
accessor: 'quantity_on_hand',
|
||||||
Cell: QuantityOnHandCell,
|
Cell: QuantityOnHandCell,
|
||||||
|
|||||||
@@ -71,6 +71,13 @@ export default [
|
|||||||
pageTitle: formatMessage({ id: 'edit_item' }),
|
pageTitle: formatMessage({ id: 'edit_item' }),
|
||||||
backLink: true,
|
backLink: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: `/items/new?duplicate=/:id`,
|
||||||
|
component: lazy({
|
||||||
|
loader: () => import('containers/Items/ItemFormPage'),
|
||||||
|
}),
|
||||||
|
breadcrumb: 'Duplicate Item',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: `/items/new`,
|
path: `/items/new`,
|
||||||
component: lazy(() => import('containers/Items/ItemFormPage')),
|
component: lazy(() => import('containers/Items/ItemFormPage')),
|
||||||
|
|||||||
Reference in New Issue
Block a user