mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 06:40:31 +00:00
re-structure to monorepo.
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
import ItemDetailTab from './ItemDetailTab';
|
||||
import ItemDetailActionsBar from './ItemDetailActionsBar';
|
||||
|
||||
/**
|
||||
* Item detail.
|
||||
*/
|
||||
export default function ItemDetail() {
|
||||
return (
|
||||
<div className="item-drawer">
|
||||
<ItemDetailActionsBar />
|
||||
<ItemDetailTab />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
Button,
|
||||
NavbarGroup,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import { useItemDetailDrawerContext } from './ItemDetailDrawerProvider';
|
||||
import { ItemAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import {
|
||||
DashboardActionsBar,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
Can,
|
||||
} from '@/components';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Item action-bar of readonly details drawer.
|
||||
*/
|
||||
function ItemDetailActionsBar({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
// Item readonly drawer context.
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
const history = useHistory();
|
||||
|
||||
// Handle edit item.
|
||||
const handleEditItem = () => {
|
||||
history.push(`/items/${itemId}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
|
||||
// Handle delete item.
|
||||
const handleDeleteItem = () => {
|
||||
openAlert('item-delete', { itemId });
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={ItemAction.Edit} a={AbilitySubject.Item}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={<T id={'edit_item'} />}
|
||||
onClick={handleEditItem}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={ItemAction.Delete} a={AbilitySubject.Item}>
|
||||
<NavbarDivider />
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
text={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={handleDeleteItem}
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DashboardActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDrawerActions,
|
||||
withAlertsActions,
|
||||
)(ItemDetailActionsBar);
|
||||
@@ -0,0 +1,24 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
import '@/style/components/Drawers/ItemDrawer.scss';
|
||||
|
||||
import { DrawerBody } from '@/components';
|
||||
import ItemContentDetails from './ItemContentDetails';
|
||||
import { ItemDetailDrawerProvider } from './ItemDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Item detail drawer content.
|
||||
*/
|
||||
export default function ItemDetailDrawerContent({
|
||||
// #ownProp
|
||||
itemId,
|
||||
}) {
|
||||
return (
|
||||
<ItemDetailDrawerProvider itemId={itemId}>
|
||||
<DrawerBody>
|
||||
<ItemContentDetails />
|
||||
</DrawerBody>
|
||||
</ItemDetailDrawerProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { DrawerHeaderContent, DrawerLoading } from '@/components';
|
||||
import { useItem } from '@/hooks/query';
|
||||
import { inactiveStatus } from './utlis';
|
||||
|
||||
const ItemDetailDrawerContext = React.createContext();
|
||||
|
||||
/**
|
||||
* Item detail provider
|
||||
*/
|
||||
function ItemDetailDrawerProvider({ itemId, ...props }) {
|
||||
// transaction type payload.
|
||||
const [value, setValue] = React.useState('invoices');
|
||||
|
||||
// Fetches the given item detail.
|
||||
const { isLoading: isItemLoading, data: item } = useItem(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
//provider.
|
||||
const provider = {
|
||||
item,
|
||||
itemId,
|
||||
isItemLoading,
|
||||
value,
|
||||
setValue,
|
||||
};
|
||||
|
||||
return (
|
||||
<DrawerLoading loading={isItemLoading}>
|
||||
<DrawerHeaderContent
|
||||
name="item-detail-drawer"
|
||||
title={inactiveStatus(item)}
|
||||
/>
|
||||
<ItemDetailDrawerContext.Provider value={provider} {...props} />
|
||||
</DrawerLoading>
|
||||
);
|
||||
}
|
||||
const useItemDetailDrawerContext = () =>
|
||||
React.useContext(ItemDetailDrawerContext);
|
||||
|
||||
export { ItemDetailDrawerProvider, useItemDetailDrawerContext };
|
||||
@@ -0,0 +1,88 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import classNames from 'classnames';
|
||||
import { defaultTo } from 'lodash';
|
||||
|
||||
import { If, DetailsMenu, DetailItem, Card } from '@/components';
|
||||
import { useItemDetailDrawerContext } from './ItemDetailDrawerProvider';
|
||||
|
||||
/**
|
||||
* Item header drawer of readonly details.
|
||||
*/
|
||||
export default function ItemDetailHeader() {
|
||||
const { item } = useItemDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div class="item-drawer__content">
|
||||
<DetailsMenu direction={'vertical'}>
|
||||
<DetailItem
|
||||
name={'name'}
|
||||
label={intl.get('item_name')}
|
||||
children={item.name}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('sell_price')}
|
||||
children={item.sell_price_formatted}
|
||||
align={'right'}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('cost_price')}
|
||||
children={item.cost_price_formatted}
|
||||
align={'right'}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
|
||||
<DetailsMenu direction={'horizantal'}>
|
||||
<DetailItem
|
||||
label={intl.get('item_type')}
|
||||
children={item.type_formatted}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('item_code')}
|
||||
children={defaultTo(item.code, '-')}
|
||||
/>
|
||||
<If condition={item.type === 'inventory'}>
|
||||
<DetailItem name={'quantity'} label={intl.get('quantity_on_hand')}>
|
||||
<span
|
||||
className={classNames({
|
||||
mines: item.quantity_on_hand <= 0,
|
||||
plus: item.quantity_on_hand > 0,
|
||||
})}
|
||||
>
|
||||
{defaultTo(item.quantity_on_hand, '-')}
|
||||
</span>
|
||||
</DetailItem>
|
||||
</If>
|
||||
<DetailItem
|
||||
label={intl.get('category_name')}
|
||||
children={defaultTo(item.category?.name, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('sell_account_id')}
|
||||
children={defaultTo(item?.sell_account?.name, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('cost_account_id')}
|
||||
children={defaultTo(item.cost_account?.name, '-')}
|
||||
/>
|
||||
<If condition={item.type === 'inventory'}>
|
||||
<DetailItem
|
||||
label={intl.get('inventory_account')}
|
||||
children={defaultTo(item?.inventory_account?.name, '-')}
|
||||
/>
|
||||
</If>
|
||||
<DetailItem
|
||||
label={intl.get('item.sell_description')}
|
||||
children={defaultTo(item.sell_description, '-')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('item.purchase_description')}
|
||||
children={defaultTo(item.purchase_description, '-')}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// @ts-nocheck
|
||||
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 '@/constants';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
|
||||
export default function ItemDetailTab() {
|
||||
const { featureCan } = useFeatureCan();
|
||||
|
||||
return (
|
||||
<DrawerMainTabs renderActiveTabPanelOnly={true}>
|
||||
<Tab
|
||||
id={'overview'}
|
||||
title={<T id={'overview'} />}
|
||||
panel={<ItemDetailHeader />}
|
||||
/>
|
||||
<Tab
|
||||
id={'transactions'}
|
||||
title={<T id={'transactions'} />}
|
||||
panel={<ItemPaymentTransactions />}
|
||||
/>
|
||||
{featureCan(Features.Warehouses) && (
|
||||
<Tab
|
||||
id={'warehouses'}
|
||||
title={<T id={'warehouse_locations.label'} />}
|
||||
panel={<WarehousesLocationsTable />}
|
||||
/>
|
||||
)}
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import clsx from 'classnames';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { Can, FormatDateCell, Icon } from '@/components';
|
||||
import { safeCallback } from '@/utils';
|
||||
import { BillAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={BillAction.Edit} a={AbilitySubject.Bill}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={BillAction.Delete} a={AbilitySubject.Bill}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve bill transactions associated with item table columns.
|
||||
*/
|
||||
export const useBillTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'bill_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_bill_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'bill_date',
|
||||
},
|
||||
{
|
||||
id: 'vendor',
|
||||
Header: intl.get('vendor'),
|
||||
accessor: 'vendor_display_name',
|
||||
width: 140,
|
||||
className: 'vendor',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'bill_number',
|
||||
Header: intl.get('bill_number'),
|
||||
accessor: (row) => (row.bill_number ? `${row.bill_number}` : null),
|
||||
width: 100,
|
||||
className: 'bill_number',
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: 'Rate',
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,74 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable, TableSkeletonRows } from '@/components';
|
||||
import { TableStyle } from '@/constants';
|
||||
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import { useItemAssociatedBillTransactions } from '@/hooks/query';
|
||||
import { useBillTransactionsColumns, ActionsMenu } from './components';
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Bill payment transactions data table.
|
||||
*/
|
||||
function BillPaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useBillTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch Estimate associated transactions.
|
||||
const {
|
||||
isLoading: isBillTransactionsLoading,
|
||||
isFetching: isBillTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedBillTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ bill_id }) => {
|
||||
openAlert('bill-delete', {
|
||||
billId: bill_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ bill_id }) => {
|
||||
history.push(`/bills/${bill_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isBillTransactionsLoading}
|
||||
headerLoading={isBillTransactionsLoading}
|
||||
progressBarLoading={isBillTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
sticky={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(BillPaymentTransactions);
|
||||
@@ -0,0 +1,100 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import clsx from 'classnames';
|
||||
import { safeCallback } from '@/utils';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { Can, FormatDateCell, Icon } from '@/components';
|
||||
import { SaleEstimateAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={SaleEstimateAction.Edit} a={AbilitySubject.Estimate}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={SaleEstimateAction.Delete} a={AbilitySubject.Estimate}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve estimate transactions associated with item table columns.
|
||||
*/
|
||||
export const useEstimateTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'estimate_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_estimate_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'estimate_date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
Header: intl.get('customer'),
|
||||
accessor: 'customer_display_name',
|
||||
width: 140,
|
||||
className: 'customer',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'estimate_number',
|
||||
Header: intl.get('estimate_no'),
|
||||
accessor: 'estimate_number',
|
||||
width: 120,
|
||||
className: 'estimate_number',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: 'Rate',
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable, TableSkeletonRows } from '@/components';
|
||||
import { TableStyle } from '@/constants';
|
||||
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import { useItemAssociatedEstimateTransactions } from '@/hooks/query';
|
||||
import { useEstimateTransactionsColumns, ActionsMenu } from './components';
|
||||
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Esimtate payment transactions.
|
||||
*/
|
||||
function EstimatePaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useEstimateTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch Estimate associated transactions.
|
||||
const {
|
||||
isLoading: isEstimateTransactionsLoading,
|
||||
isFetching: isEstimateTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedEstimateTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ estimate_id }) => {
|
||||
openAlert('estimate-delete', {
|
||||
estimateId: estimate_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ estimate_id }) => {
|
||||
history.push(`/estimates/${estimate_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isEstimateTransactionsLoading}
|
||||
headerLoading={isEstimateTransactionsLoading}
|
||||
progressBarLoading={isEstimateTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
sticky={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(EstimatePaymentTransactions);
|
||||
@@ -0,0 +1,100 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import clsx from 'classnames';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { Can, FormatDateCell, Icon } from '@/components';
|
||||
import { safeCallback } from '@/utils';
|
||||
import { SaleInvoiceAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={SaleInvoiceAction.Edit} a={AbilitySubject.Invoice}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={SaleInvoiceAction.Delete} a={AbilitySubject.Invoice}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve invoice payment transactions associated with item table columns.
|
||||
*/
|
||||
export const useInvoicePaymentTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'invoice_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_invoice_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'invoice_date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
Header: intl.get('customer'),
|
||||
accessor: 'customer_display_name',
|
||||
width: 140,
|
||||
className: 'customer',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'invoice_no',
|
||||
Header: intl.get('invoice_no__'),
|
||||
accessor: 'invoice_number',
|
||||
width: 120,
|
||||
className: 'invoice_no',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: intl.get('rate'),
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { DataTable, TableSkeletonRows } from '@/components';
|
||||
|
||||
import { useItemAssociatedInvoiceTransactions } from '@/hooks/query';
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import {
|
||||
useInvoicePaymentTransactionsColumns,
|
||||
ActionsMenu,
|
||||
} from './components';
|
||||
import { TableStyle } from '@/constants';
|
||||
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Invoice payment transactions.
|
||||
*/
|
||||
function InvoicePaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useInvoicePaymentTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch invoice associated transactions.
|
||||
const {
|
||||
isLoading: isInvoiceTransactionsLoading,
|
||||
isFetching: isInvoiceTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedInvoiceTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ invoice_id }) => {
|
||||
openAlert('invoice-delete', {
|
||||
invoiceId: invoice_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ invoice_id }) => {
|
||||
history.push(`/invoices/${invoice_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isInvoiceTransactionsLoading}
|
||||
headerLoading={isInvoiceTransactionsLoading}
|
||||
progressBarLoading={isInvoiceTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
sticky={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(InvoicePaymentTransactions);
|
||||
@@ -0,0 +1,23 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import InvoicePaymentTransactions from './InvoicePaymentTransactions';
|
||||
import EstimatePaymentTransactions from './EstimatePaymentTransactions';
|
||||
import ReceiptPaymentTransactions from './ReceiptPaymentTransactions';
|
||||
import BillPaymentTransactions from './BillPaymentTransactions';
|
||||
|
||||
export default function ItemPaymentTransactionsContent({ tansactionType }) {
|
||||
const handleType = () => {
|
||||
switch (tansactionType) {
|
||||
case 'invoices':
|
||||
default:
|
||||
return <InvoicePaymentTransactions />;
|
||||
case 'estimates':
|
||||
return <EstimatePaymentTransactions />;
|
||||
case 'receipts':
|
||||
return <ReceiptPaymentTransactions />;
|
||||
case 'bills':
|
||||
return <BillPaymentTransactions />;
|
||||
}
|
||||
};
|
||||
return handleType();
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Menu, MenuItem, MenuDivider } from '@blueprintjs/core';
|
||||
|
||||
import clsx from 'classnames';
|
||||
import { safeCallback } from '@/utils';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { Can, FormatDateCell, Icon } from '@/components';
|
||||
import { SaleReceiptAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={SaleReceiptAction.Edit} a={AbilitySubject.Receipt}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={SaleReceiptAction.Edit} a={AbilitySubject.Receipt}>
|
||||
<MenuDivider />
|
||||
<MenuItem
|
||||
text={intl.get('invoice_transactions.action.delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
/>
|
||||
</Can>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve receipt transactions associated with item table columns.
|
||||
*/
|
||||
export const useReceiptTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'receipt_date',
|
||||
Header: intl.get('date'),
|
||||
accessor: 'formatted_receipt_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 120,
|
||||
className: 'receipt_date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
Header: intl.get('customer'),
|
||||
accessor: 'customer_display_name',
|
||||
width: 140,
|
||||
className: 'customer',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'receipt_number',
|
||||
Header: intl.get('receipt_no'),
|
||||
accessor: 'receip_number',
|
||||
width: 120,
|
||||
className: 'receipt_number',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'qunatity',
|
||||
Header: intl.get('item.drawer_quantity_sold'),
|
||||
accessor: 'quantity',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'rate',
|
||||
Header: 'Rate',
|
||||
accessor: 'formatted_rate',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('total'),
|
||||
accessor: 'formatted_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,76 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import { TableStyle } from '@/constants';
|
||||
import { DataTable, TableSkeletonRows } from '@/components';
|
||||
import { useItemDetailDrawerContext } from '../../ItemDetailDrawerProvider';
|
||||
import { useItemAssociatedReceiptTransactions } from '@/hooks/query';
|
||||
import { useReceiptTransactionsColumns, ActionsMenu } from './components';
|
||||
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Receipt payment transactions.
|
||||
*/
|
||||
function ReceiptPaymentTransactions({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useReceiptTransactionsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch receipts associated transactions.
|
||||
const {
|
||||
isLoading: isReceiptTransactionsLoading,
|
||||
isFetching: isReceiptTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useItemAssociatedReceiptTransactions(itemId, {
|
||||
enabled: !!itemId,
|
||||
});
|
||||
|
||||
// Handles delete payment transactions.
|
||||
const handleDeletePaymentTransactons = ({ receipt_id }) => {
|
||||
openAlert('receipt-delete', {
|
||||
receiptId: receipt_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit payment transactions.
|
||||
const handleEditPaymentTransactions = ({ receipt_id }) => {
|
||||
history.push(`/receipts/${receipt_id}/edit`);
|
||||
closeDrawer('item-detail-drawer');
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isReceiptTransactionsLoading}
|
||||
headerLoading={isReceiptTransactionsLoading}
|
||||
progressBarLoading={isReceiptTransactionFetching}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onEdit: handleEditPaymentTransactions,
|
||||
onDelete: handleDeletePaymentTransactons,
|
||||
}}
|
||||
styleName={TableStyle.Constrant}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
sticky={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(ReceiptPaymentTransactions);
|
||||
@@ -0,0 +1,43 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from '@/components';
|
||||
import { ItemManuTransaction } from './utils';
|
||||
import ItemPaymentTransactionContent from './ItemPaymentTransactionContent';
|
||||
|
||||
import { useItemDetailDrawerContext } from '../ItemDetailDrawerProvider';
|
||||
|
||||
export function ItemPaymentTransactions() {
|
||||
const { value } = useItemDetailDrawerContext();
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<ItemTransactionsHeader />
|
||||
<ItemPaymentTransactionContent tansactionType={value} />
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Item transactions header.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function ItemTransactionsHeader() {
|
||||
const { setValue } = useItemDetailDrawerContext();
|
||||
|
||||
// handle item change.
|
||||
const handleItemChange = (item) => {
|
||||
setValue(item);
|
||||
};
|
||||
|
||||
return (
|
||||
<ItemTransactionsHeaderRoot>
|
||||
<ItemManuTransaction onChange={handleItemChange} />
|
||||
</ItemTransactionsHeaderRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const ItemTransactionsHeaderRoot = styled.div`
|
||||
margin-bottom: 10px;
|
||||
`;
|
||||
@@ -0,0 +1,66 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Button,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from '@/components';
|
||||
import { useItemDetailDrawerContext } from '../ItemDetailDrawerProvider';
|
||||
import { useGetItemPaymentTransactionsMenu } from '@/constants/itemPaymentTranactionsOption';
|
||||
|
||||
export const ItemManuTransaction = ({ onChange }) => {
|
||||
const { value, setValue } = useItemDetailDrawerContext();
|
||||
const itemTransactionMenu = useGetItemPaymentTransactionsMenu();
|
||||
|
||||
if (itemTransactionMenu.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleClickItem = (item) => {
|
||||
onChange && onChange(item);
|
||||
};
|
||||
|
||||
const content = itemTransactionMenu.map(({ name, label }) => (
|
||||
<MenuItem onClick={() => handleClickItem(name)} text={label} />
|
||||
));
|
||||
|
||||
return (
|
||||
<Popover
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
content={<Menu>{content}</Menu>}
|
||||
>
|
||||
<ItemSwitchButton
|
||||
minimal={true}
|
||||
text={<T id={'item.drawer_transactions_by'} />}
|
||||
rightIcon={'caret-down'}
|
||||
>
|
||||
<ItemSwitchText>
|
||||
<T id={value} />
|
||||
</ItemSwitchText>
|
||||
</ItemSwitchButton>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
const ItemSwitchButton = styled(Button)`
|
||||
.bp3-button-text {
|
||||
display: flex;
|
||||
color: #727983;
|
||||
}
|
||||
`;
|
||||
|
||||
const ItemSwitchText = styled.span`
|
||||
font-weight: 600;
|
||||
color: #33304a;
|
||||
padding-left: 3px;
|
||||
`;
|
||||
@@ -0,0 +1,41 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { FormatNumberCell, FormattedMessage as T } from '@/components';
|
||||
|
||||
/**
|
||||
* Warehouse locations columns
|
||||
*/
|
||||
export const useWarehouseLocationsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'warehouse_name',
|
||||
accessor: 'warehouse_name',
|
||||
Header: intl.get('warehouse_locations.column.warehouse_name'),
|
||||
width: 120,
|
||||
},
|
||||
{
|
||||
id: 'warehouse_code',
|
||||
accessor: 'warehouse_code',
|
||||
Header: intl.get('warehouse_locations.column.warehouse_code'),
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'quantity',
|
||||
accessor: 'quantity_on_hand_formatted',
|
||||
Header: intl.get('warehouse_locations.column.quantity'),
|
||||
Cell: FormatNumberCell,
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
id: 'available_for_sale',
|
||||
Header: intl.get('warehouse_locations.column.available_for_sale'),
|
||||
align: 'right',
|
||||
width: 100,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { TableStyle } from '@/constants';
|
||||
import { DataTable, TableSkeletonRows, Card } from '@/components';
|
||||
|
||||
import { useItemDetailDrawerContext } from '../ItemDetailDrawerProvider';
|
||||
import { useWarehouseLocationsColumns } from './components';
|
||||
import { useItemWarehouseLocation } from '@/hooks/query';
|
||||
|
||||
/**
|
||||
* Warehouses locations table columns.
|
||||
*/
|
||||
export default function WarehouseLocationsTable() {
|
||||
// Warehouses locations table columns.
|
||||
const columns = useWarehouseLocationsColumns();
|
||||
|
||||
const { itemId } = useItemDetailDrawerContext();
|
||||
|
||||
// Handle fetch Estimate associated transactions.
|
||||
const {
|
||||
isLoading: isItemWarehousesLoading,
|
||||
isFetching: isItemWarehousesFetching,
|
||||
data: itemWarehouses,
|
||||
} = useItemWarehouseLocation(itemId, { enabled: !!itemId });
|
||||
return (
|
||||
<WarehouseLocationsGLEntriesRoot>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={itemWarehouses}
|
||||
headerLoading={isItemWarehousesLoading}
|
||||
progressBarLoading={isItemWarehousesFetching}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
</WarehouseLocationsGLEntriesRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const WarehouseLocationsGLEntriesRoot = styled(Card)``;
|
||||
@@ -0,0 +1,35 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Drawer, DrawerSuspense } from '@/components';
|
||||
import withDrawers from '@/containers/Drawer/withDrawers';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
const ItemDetailDrawerContent = React.lazy(() =>
|
||||
import('./ItemDetailDrawerContent'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Item Detail drawer.
|
||||
*/
|
||||
function ItemDetailDrawer({
|
||||
name,
|
||||
|
||||
// #withDrawer
|
||||
isOpen,
|
||||
payload: { itemId },
|
||||
}) {
|
||||
return (
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
size={'65%'}
|
||||
>
|
||||
<DrawerSuspense>
|
||||
<ItemDetailDrawerContent itemId={itemId} />
|
||||
</DrawerSuspense>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
export default compose(withDrawers())(ItemDetailDrawer);
|
||||
@@ -0,0 +1,28 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Intent, Tag } from '@blueprintjs/core';
|
||||
import { Choose, FormattedMessage as T } from '@/components';
|
||||
|
||||
/**
|
||||
* items inactive status.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function inactiveStatus(item) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={!item.active}>
|
||||
{item.name}
|
||||
<StatusTag intent={Intent.NONE} minimal={true} round={true}>
|
||||
<T id={'item.details.inactive'} />
|
||||
</StatusTag>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>{item.name}</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusTag = styled(Tag)`
|
||||
font-size: 11px;
|
||||
margin-left: 10px;
|
||||
`;
|
||||
Reference in New Issue
Block a user