mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-19 06:10:31 +00:00
re-structure to monorepo.
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
Button,
|
||||
NavbarGroup,
|
||||
Classes,
|
||||
NavbarDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import {
|
||||
Can,
|
||||
If,
|
||||
Icon,
|
||||
DrawerActionsBar,
|
||||
FormattedMessage as T,
|
||||
} from '@/components';
|
||||
import {
|
||||
BillAction,
|
||||
PaymentMadeAction,
|
||||
AbilitySubject,
|
||||
} from '@/constants/abilityOption';
|
||||
import { BillMenuItem } from './utils';
|
||||
|
||||
import { safeCallback, compose } from '@/utils';
|
||||
|
||||
function BillDetailActionsBar({
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const { billId, bill } = useBillDrawerContext();
|
||||
|
||||
// Handle edit bill.
|
||||
const onEditBill = () => {
|
||||
history.push(`/bills/${billId}/edit`);
|
||||
closeDrawer('bill-drawer');
|
||||
};
|
||||
|
||||
// Handle convert to vendor credit.
|
||||
const handleConvertToVendorCredit = () => {
|
||||
history.push(`/vendor-credits/new?from_bill_id=${billId}`, {
|
||||
billId: billId,
|
||||
});
|
||||
closeDrawer('bill-drawer');
|
||||
};
|
||||
|
||||
// Handle delete bill.
|
||||
const onDeleteBill = () => {
|
||||
openAlert('bill-delete', { billId });
|
||||
};
|
||||
|
||||
// Handle quick bill payment .
|
||||
const handleQuickBillPayment = () => {
|
||||
openDialog('quick-payment-made', { billId });
|
||||
};
|
||||
|
||||
// Handle allocate landed cost button click.
|
||||
const handleAllocateCostClick = () => {
|
||||
openDialog('allocate-landed-cost', { billId });
|
||||
};
|
||||
|
||||
return (
|
||||
<DrawerActionsBar>
|
||||
<NavbarGroup>
|
||||
<Can I={BillAction.Edit} a={AbilitySubject.Bill}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={<T id={'edit_bill'} />}
|
||||
onClick={safeCallback(onEditBill)}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</Can>
|
||||
<Can I={PaymentMadeAction.Create} a={AbilitySubject.PaymentMade}>
|
||||
<If condition={bill.is_open && !bill.is_fully_paid}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="arrow-upward" iconSize={16} />}
|
||||
text={<T id={'add_payment'} />}
|
||||
onClick={handleQuickBillPayment}
|
||||
/>
|
||||
</If>
|
||||
</Can>
|
||||
<Can I={BillAction.Delete} a={AbilitySubject.Bill}>
|
||||
<NavbarDivider />
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
text={<T id={'delete'} />}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDeleteBill)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={BillAction.Edit} a={AbilitySubject.Bill}>
|
||||
<NavbarDivider />
|
||||
<BillMenuItem
|
||||
payload={{
|
||||
onConvert: handleConvertToVendorCredit,
|
||||
onAllocateLandedCost: handleAllocateCostClick,
|
||||
}}
|
||||
/>
|
||||
</Can>
|
||||
</NavbarGroup>
|
||||
</DrawerActionsBar>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withDrawerActions,
|
||||
withAlertsActions,
|
||||
)(BillDetailActionsBar);
|
||||
@@ -0,0 +1,28 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import {
|
||||
CommercialDocFooter,
|
||||
T,
|
||||
If,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
} from '@/components';
|
||||
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
/**
|
||||
* Bill detail footer.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function BillDetailFooter() {
|
||||
const { bill } = useBillDrawerContext();
|
||||
return (
|
||||
<CommercialDocFooter>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<If condition={bill.note}>
|
||||
<DetailItem label={<T id={'note'} />}>{bill.note}</DetailItem>
|
||||
</If>
|
||||
</DetailsMenu>
|
||||
</CommercialDocFooter>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import { defaultTo } from 'lodash';
|
||||
|
||||
import {
|
||||
FormatDate,
|
||||
DetailsMenu,
|
||||
DetailItem,
|
||||
Row,
|
||||
Col,
|
||||
CommercialDocHeader,
|
||||
CommercialDocTopHeader,
|
||||
VendorDrawerLink,
|
||||
ExchangeRateDetailItem,
|
||||
} from '@/components';
|
||||
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
import { BillDetailsStatus } from './utils';
|
||||
|
||||
/**
|
||||
* Bill detail header.
|
||||
*/
|
||||
export default function BillDetailHeader() {
|
||||
const { bill } = useBillDrawerContext();
|
||||
|
||||
return (
|
||||
<CommercialDocHeader>
|
||||
<CommercialDocTopHeader>
|
||||
<DetailsMenu>
|
||||
<AmountDetailItem label={intl.get('amount')}>
|
||||
<h3 class="big-number">{bill.formatted_amount}</h3>
|
||||
</AmountDetailItem>
|
||||
<StatusDetailItem>
|
||||
<BillDetailsStatus bill={bill} />
|
||||
</StatusDetailItem>
|
||||
</DetailsMenu>
|
||||
</CommercialDocTopHeader>
|
||||
<Row>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu direction={'horizantal'} minLabelSize={'180px'}>
|
||||
<DetailItem label={intl.get('bill_date')}>
|
||||
<FormatDate value={bill.bill_date} />
|
||||
</DetailItem>
|
||||
<DetailItem label={intl.get('due_date')}>
|
||||
<FormatDate value={bill.due_date} />
|
||||
</DetailItem>
|
||||
<DetailItem label={intl.get('vendor_name')}>
|
||||
<VendorDrawerLink vendorId={bill.vendor_id}>
|
||||
{bill.vendor?.display_name}
|
||||
</VendorDrawerLink>
|
||||
</DetailItem>
|
||||
<DetailItem label={intl.get('bill.details.bill_number')}>
|
||||
{defaultTo(bill.bill_number, '-')}
|
||||
</DetailItem>
|
||||
<ExchangeRateDetailItem
|
||||
exchangeRate={bill?.exchange_rate}
|
||||
toCurrency={bill?.currency_code}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
<Col xs={6}>
|
||||
<DetailsMenu
|
||||
direction={'horizantal'}
|
||||
minLabelSize={'140px'}
|
||||
textAlign={'right'}
|
||||
>
|
||||
<DetailItem label={intl.get('due_amount')}>
|
||||
<strong>{bill.formatted_due_amount}</strong>
|
||||
</DetailItem>
|
||||
<DetailItem
|
||||
label={intl.get('reference')}
|
||||
children={defaultTo(bill.reference_no, '--')}
|
||||
/>
|
||||
<DetailItem
|
||||
label={intl.get('bill.details.created_at')}
|
||||
children={<FormatDate value={bill.created_at} />}
|
||||
/>
|
||||
</DetailsMenu>
|
||||
</Col>
|
||||
</Row>
|
||||
</CommercialDocHeader>
|
||||
);
|
||||
}
|
||||
|
||||
const StatusDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
text-align: right;
|
||||
position: relative;
|
||||
top: -5px;
|
||||
`;
|
||||
|
||||
const AmountDetailItem = styled(DetailItem)`
|
||||
width: 50%;
|
||||
`;
|
||||
@@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
import BillDetailHeader from './BillDetailHeader';
|
||||
import BillDetailTable from './BillDetailTable';
|
||||
import BillDetailFooter from './BillDetailFooter';
|
||||
import { CommercialDocBox } from '@/components';
|
||||
import { BillDetailTableFooter } from './BillDetailTableFooter';
|
||||
|
||||
/**
|
||||
* Bill detail panel tab.
|
||||
*/
|
||||
export default function BillDetailTab() {
|
||||
return (
|
||||
<CommercialDocBox>
|
||||
<BillDetailHeader />
|
||||
<BillDetailTable />
|
||||
<BillDetailTableFooter />
|
||||
<BillDetailFooter />
|
||||
</CommercialDocBox>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
|
||||
import { CommercialDocEntriesTable } from '@/components';
|
||||
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
import { useBillReadonlyEntriesTableColumns } from './utils';
|
||||
|
||||
import { TableStyle } from '@/constants';
|
||||
|
||||
export default function BillDetailTable() {
|
||||
const {
|
||||
bill: { entries },
|
||||
} = useBillDrawerContext();
|
||||
|
||||
// Retrieve bill readonly entries table columns.
|
||||
const columns = useBillReadonlyEntriesTableColumns();
|
||||
|
||||
return (
|
||||
<CommercialDocEntriesTable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
styleName={TableStyle.Constrant}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import {
|
||||
TotalLineBorderStyle,
|
||||
TotalLineTextStyle,
|
||||
FormatNumber,
|
||||
T,
|
||||
TotalLines,
|
||||
TotalLine,
|
||||
} from '@/components';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
/**
|
||||
* Bill read-only details table footer.
|
||||
*/
|
||||
export function BillDetailTableFooter() {
|
||||
const { bill } = useBillDrawerContext();
|
||||
|
||||
return (
|
||||
<BillDetailsFooterRoot>
|
||||
<BillTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.subtotal'} />}
|
||||
value={<FormatNumber value={bill.amont} />}
|
||||
borderStyle={TotalLineBorderStyle.SingleDark}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.total'} />}
|
||||
value={bill.formatted_amount}
|
||||
borderStyle={TotalLineBorderStyle.DoubleDark}
|
||||
textStyle={TotalLineTextStyle.Bold}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.payment_amount'} />}
|
||||
value={bill.formatted_payment_amount}
|
||||
/>
|
||||
<TotalLine
|
||||
title={<T id={'bill.details.due_amount'} />}
|
||||
value={bill.formatted_due_amount}
|
||||
/>
|
||||
</BillTotalLines>
|
||||
</BillDetailsFooterRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const BillDetailsFooterRoot = styled.div``;
|
||||
|
||||
export const BillTotalLines = styled(TotalLines)`
|
||||
margin-left: auto;
|
||||
`;
|
||||
@@ -0,0 +1,22 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { DrawerBody } from '@/components';
|
||||
|
||||
import { BillDrawerProvider } from './BillDrawerProvider';
|
||||
import BillDrawerDetails from './BillDrawerDetails';
|
||||
|
||||
/**
|
||||
* Bill drawer content.
|
||||
*/
|
||||
export default function BillDrawerContent({
|
||||
// #ownProp
|
||||
billId,
|
||||
}) {
|
||||
return (
|
||||
<BillDrawerProvider billId={billId}>
|
||||
<DrawerBody>
|
||||
<BillDrawerDetails />
|
||||
</DrawerBody>
|
||||
</BillDrawerProvider>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Tab } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DrawerMainTabs } from '@/components';
|
||||
import { useAbilityContext } from '@/hooks/utils';
|
||||
import { PaymentMadeAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
import BillDetailTab from './BillDetailTab';
|
||||
import LocatedLandedCostTable from './LocatedLandedCostTable';
|
||||
import BillGLEntriesTable from './BillGLEntriesTable';
|
||||
import BillPaymentTransactionTable from './BillPaymentTransactions/BillPaymentTransactionTable';
|
||||
import BillDetailActionsBar from './BillDetailActionsBar';
|
||||
|
||||
/**
|
||||
* Bill details tabs.
|
||||
*/
|
||||
function BillDetailsTabs() {
|
||||
const ability = useAbilityContext();
|
||||
|
||||
return (
|
||||
<DrawerMainTabs
|
||||
renderActiveTabPanelOnly={true}
|
||||
defaultSelectedTabId="details"
|
||||
>
|
||||
<Tab
|
||||
title={intl.get('overview')}
|
||||
id={'details'}
|
||||
panel={<BillDetailTab />}
|
||||
/>
|
||||
<Tab
|
||||
title={intl.get('journal_entries')}
|
||||
id={'journal_entries'}
|
||||
panel={<BillGLEntriesTable />}
|
||||
/>
|
||||
{ability.can(PaymentMadeAction.View, AbilitySubject.PaymentMade) && (
|
||||
<Tab
|
||||
title={intl.get('payment_transactions')}
|
||||
id={'payment_transactions'}
|
||||
panel={<BillPaymentTransactionTable />}
|
||||
/>
|
||||
)}
|
||||
<Tab
|
||||
title={intl.get('located_landed_cost')}
|
||||
id={'landed_cost'}
|
||||
panel={<LocatedLandedCostTable />}
|
||||
/>
|
||||
</DrawerMainTabs>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bill view detail.
|
||||
*/
|
||||
export default function BillDetails() {
|
||||
return (
|
||||
<BillDetailsRoot>
|
||||
<BillDetailActionsBar />
|
||||
<BillDetailsTabs />
|
||||
</BillDetailsRoot>
|
||||
);
|
||||
}
|
||||
|
||||
export const BillDetailsRoot = styled.div``;
|
||||
@@ -0,0 +1,60 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { DrawerHeaderContent, DrawerLoading } from '@/components';
|
||||
import { useBill, useBillLocatedLandedCost } from '@/hooks/query';
|
||||
import { useFeatureCan } from '@/hooks/state';
|
||||
import { Features } from '@/constants';
|
||||
|
||||
const BillDrawerContext = React.createContext();
|
||||
|
||||
/**
|
||||
* Bill drawer provider.
|
||||
*/
|
||||
function BillDrawerProvider({ billId, ...props }) {
|
||||
// Features guard.
|
||||
const { featureCan } = useFeatureCan();
|
||||
|
||||
// Handle fetch bill details.
|
||||
const { isLoading: isBillLoading, data: bill } = useBill(billId, {
|
||||
enabled: !!billId,
|
||||
});
|
||||
|
||||
// Handle fetch bill located landed cost transaction.
|
||||
const { isLoading: isLandedCostLoading, data: transactions } =
|
||||
useBillLocatedLandedCost(billId, {
|
||||
enabled: !!billId,
|
||||
});
|
||||
|
||||
//provider.
|
||||
const provider = {
|
||||
billId,
|
||||
transactions,
|
||||
bill,
|
||||
};
|
||||
|
||||
const loading = isLandedCostLoading || isBillLoading;
|
||||
|
||||
return (
|
||||
<DrawerLoading loading={loading}>
|
||||
<DrawerHeaderContent
|
||||
name="bill-drawer"
|
||||
title={intl.get('bill.drawer.title', {
|
||||
number: bill.bill_number ? `(${bill.bill_number})` : null,
|
||||
})}
|
||||
subTitle={
|
||||
featureCan(Features.Branches)
|
||||
? intl.get('bill.drawer.subtitle', {
|
||||
value: bill.branch?.name,
|
||||
})
|
||||
: null
|
||||
}
|
||||
/>
|
||||
<BillDrawerContext.Provider value={provider} {...props} />
|
||||
</DrawerLoading>
|
||||
);
|
||||
}
|
||||
|
||||
const useBillDrawerContext = () => React.useContext(BillDrawerContext);
|
||||
|
||||
export { BillDrawerProvider, useBillDrawerContext };
|
||||
@@ -0,0 +1,50 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { Card } from '@/components';
|
||||
|
||||
import { useTransactionsByReference } from '@/hooks/query';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
import JournalEntriesTable, {
|
||||
AmountDisplayedBaseCurrencyMessage,
|
||||
} from '../../JournalEntriesTable/JournalEntriesTable';
|
||||
|
||||
/**
|
||||
* Bill GL entries table.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export default function BillGLEntriesTable() {
|
||||
const { billId } = useBillDrawerContext();
|
||||
|
||||
// Handle fetch transaction by reference.
|
||||
const {
|
||||
data: { transactions },
|
||||
isLoading: isTransactionLoading,
|
||||
} = useTransactionsByReference(
|
||||
{
|
||||
reference_id: billId,
|
||||
reference_type: 'Bill',
|
||||
},
|
||||
{ enabled: !!billId },
|
||||
);
|
||||
|
||||
return (
|
||||
<BilleGLEntriesRoot>
|
||||
<AmountDisplayedBaseCurrencyMessage />
|
||||
<BillGLEntriesDatatable
|
||||
loading={isTransactionLoading}
|
||||
transactions={transactions}
|
||||
/>
|
||||
</BilleGLEntriesRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const BilleGLEntriesRoot = styled(Card)``;
|
||||
|
||||
const BillGLEntriesDatatable = styled(JournalEntriesTable)`
|
||||
.table .tbody .tr:last-child .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
`;
|
||||
@@ -0,0 +1,77 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { DataTable, Card, TableSkeletonRows } from '@/components';
|
||||
|
||||
import { TableStyle } from '@/constants';
|
||||
import { useBillPaymentTransactionsColumns, ActionsMenu } from './components';
|
||||
import { useBillDrawerContext } from '../BillDrawerProvider';
|
||||
import { useBillPaymentTransactions } from '@/hooks/query';
|
||||
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Bill payment transactions datatable.
|
||||
*/
|
||||
function BillPaymentTransactionTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const columns = useBillPaymentTransactionsColumns();
|
||||
|
||||
const { billId } = useBillDrawerContext();
|
||||
|
||||
// Handle fetch bill payment transaction.
|
||||
const {
|
||||
isLoading: isPaymentTransactionsLoading,
|
||||
isFetching: isPaymentTransactionFetching,
|
||||
data: paymentTransactions,
|
||||
} = useBillPaymentTransactions(billId, {
|
||||
enabled: !!billId,
|
||||
});
|
||||
|
||||
// Handles delete bill payment transactions.
|
||||
const handleDeleteBillPaymentTransactons = ({ bill_payment_id }) => {
|
||||
openAlert('payment-made-delete', {
|
||||
paymentMadeId: bill_payment_id,
|
||||
});
|
||||
};
|
||||
|
||||
// Handles edit bill payment transactions.
|
||||
const handleEditBillPaymentTransactions = ({ bill_payment_id }) => {
|
||||
history.push(`/payment-mades/${bill_payment_id}/edit`);
|
||||
closeDrawer('bill-drawer');
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={paymentTransactions}
|
||||
loading={isPaymentTransactionsLoading}
|
||||
headerLoading={isPaymentTransactionsLoading}
|
||||
progressBarLoading={isPaymentTransactionFetching}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
styleName={TableStyle.Constrant}
|
||||
ContextMenu={ActionsMenu}
|
||||
payload={{
|
||||
onDelete: handleDeleteBillPaymentTransactons,
|
||||
onEdit: handleEditBillPaymentTransactions,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
)(BillPaymentTransactionTable);
|
||||
@@ -0,0 +1,90 @@
|
||||
// @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 { safeCallback } from '@/utils';
|
||||
import { Can, FormatDateCell, Icon } from '@/components';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { PaymentMadeAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
|
||||
/**
|
||||
* Table actions menu.
|
||||
*/
|
||||
export function ActionsMenu({
|
||||
row: { original },
|
||||
payload: { onEdit, onDelete },
|
||||
}) {
|
||||
return (
|
||||
<Menu>
|
||||
<Can I={PaymentMadeAction.Edit} a={AbilitySubject.PaymentMade}>
|
||||
<MenuItem
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('invoice_transactions.action.edit_transaction')}
|
||||
onClick={safeCallback(onEdit, original)}
|
||||
/>
|
||||
</Can>
|
||||
<Can I={PaymentMadeAction.Delete} a={AbilitySubject.PaymentMade}>
|
||||
<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 payment transactions table columns.
|
||||
*/
|
||||
export const useBillPaymentTransactionsColumns = () => {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'date',
|
||||
Header: intl.get('payment_date'),
|
||||
accessor: 'formatted_payment_date',
|
||||
Cell: FormatDateCell,
|
||||
width: 110,
|
||||
className: 'date',
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'payment_account_name',
|
||||
Header: intl.get('bill_transactions.column.deposit_account'),
|
||||
accessor: 'payment_account_name',
|
||||
width: 120,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'amount',
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formatted_payment_amount',
|
||||
align: 'right',
|
||||
width: 100,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'payment_number',
|
||||
Header: intl.get('payment_no'),
|
||||
accessor: 'payment_number',
|
||||
width: 100,
|
||||
className: 'payment_number',
|
||||
},
|
||||
{
|
||||
id: 'reference',
|
||||
Header: intl.get('reference_no'),
|
||||
accessor: 'payment_reference_no',
|
||||
width: 90,
|
||||
className: 'payment_reference_no',
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,84 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import {
|
||||
DataTable,
|
||||
TableSkeletonRows,
|
||||
Card,
|
||||
FormattedMessage as T,
|
||||
} from '@/components';
|
||||
|
||||
import { useLocatedLandedCostColumns, ActionsMenu } from './components';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
import withAlertsActions from '@/containers/Alert/withAlertActions';
|
||||
import withDialogActions from '@/containers/Dialog/withDialogActions';
|
||||
import withDrawerActions from '@/containers/Drawer/withDrawerActions';
|
||||
|
||||
import { TableStyle } from '@/constants';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
/**
|
||||
* Located landed cost table.
|
||||
*/
|
||||
function LocatedLandedCostTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
// Located landed cost table columns.
|
||||
const columns = useLocatedLandedCostColumns();
|
||||
|
||||
// Bill drawer context.
|
||||
const { transactions, billId } = useBillDrawerContext();
|
||||
|
||||
// Handle the transaction delete action.
|
||||
const handleDeleteTransaction = ({ id }) => {
|
||||
openAlert('bill-located-cost-delete', { BillId: id });
|
||||
};
|
||||
|
||||
// Handle from transaction link click.
|
||||
const handleFromTransactionClick = (original) => {
|
||||
const { from_transaction_type, from_transaction_id } = original;
|
||||
|
||||
switch (from_transaction_type) {
|
||||
case 'Expense':
|
||||
openDrawer('expense-drawer', { expenseId: from_transaction_id });
|
||||
break;
|
||||
|
||||
case 'Bill':
|
||||
default:
|
||||
openDrawer('bill-drawer', { billId: from_transaction_id });
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<DataTable
|
||||
columns={columns}
|
||||
data={transactions}
|
||||
ContextMenu={ActionsMenu}
|
||||
TableLoadingRenderer={TableSkeletonRows}
|
||||
styleName={TableStyle.Constrant}
|
||||
payload={{
|
||||
onDelete: handleDeleteTransaction,
|
||||
onFromTranscationClick: handleFromTransactionClick,
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withAlertsActions,
|
||||
withDialogActions,
|
||||
withDrawerActions,
|
||||
)(LocatedLandedCostTable);
|
||||
103
packages/webapp/src/containers/Drawers/BillDrawer/components.tsx
Normal file
103
packages/webapp/src/containers/Drawers/BillDrawer/components.tsx
Normal file
@@ -0,0 +1,103 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import clsx from 'classnames';
|
||||
import { Intent, MenuItem, Menu } from '@blueprintjs/core';
|
||||
import { safeCallback } from '@/utils';
|
||||
import { CLASSES } from '@/constants/classes';
|
||||
import { Icon } from '@/components';
|
||||
|
||||
/**
|
||||
* Actions menu.
|
||||
*/
|
||||
export function ActionsMenu({ row: { original }, payload: { onDelete } }) {
|
||||
return (
|
||||
<Menu>
|
||||
<MenuItem
|
||||
icon={<Icon icon="trash-16" iconSize={16} />}
|
||||
text={intl.get('delete_transaction')}
|
||||
intent={Intent.DANGER}
|
||||
onClick={safeCallback(onDelete, original)}
|
||||
/>
|
||||
</Menu>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* From transaction table cell.
|
||||
*/
|
||||
export function FromTransactionCell({
|
||||
row: { original },
|
||||
payload: { onFromTranscationClick },
|
||||
}) {
|
||||
// Handle the link click
|
||||
const handleAnchorClick = () => {
|
||||
onFromTranscationClick && onFromTranscationClick(original);
|
||||
};
|
||||
|
||||
return (
|
||||
<a href="#" onClick={handleAnchorClick}>
|
||||
{original.from_transaction_type} → {original.from_transaction_id}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Name accessor.
|
||||
*/
|
||||
export const NameAccessor = (row) => {
|
||||
return (
|
||||
<span className="name">
|
||||
<LabelName>{row.name}</LabelName>
|
||||
<LabelDescription>{row.description}</LabelDescription>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieve bill located landed cost table columns.
|
||||
*/
|
||||
export function useLocatedLandedCostColumns() {
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('name'),
|
||||
accessor: NameAccessor,
|
||||
width: 150,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'formatted_amount',
|
||||
width: 100,
|
||||
align: 'right',
|
||||
textOverview: true,
|
||||
className: clsx(CLASSES.FONT_BOLD),
|
||||
},
|
||||
{
|
||||
id: 'from_transaction',
|
||||
Header: intl.get('From transaction'),
|
||||
Cell: FromTransactionCell,
|
||||
width: 100,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('allocation_method'),
|
||||
accessor: 'allocation_method_formatted',
|
||||
width: 100,
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
const LabelName = styled.div``;
|
||||
|
||||
const LabelDescription = styled.div`
|
||||
font-size: 12px;
|
||||
margin-top: 2px;
|
||||
display: block;
|
||||
opacity: 0.75;
|
||||
`;
|
||||
33
packages/webapp/src/containers/Drawers/BillDrawer/index.tsx
Normal file
33
packages/webapp/src/containers/Drawers/BillDrawer/index.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { Drawer, DrawerSuspense } from '@/components';
|
||||
import withDrawers from '@/containers/Drawer/withDrawers';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
|
||||
const BillDrawerContent = React.lazy(() => import('./BillDrawerContent'));
|
||||
|
||||
/**
|
||||
* Bill drawer.
|
||||
*/
|
||||
function BillDrawer({
|
||||
name,
|
||||
// #withDrawer
|
||||
isOpen,
|
||||
payload: { billId },
|
||||
}) {
|
||||
return (
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
size={'65%'}
|
||||
>
|
||||
<DrawerSuspense>
|
||||
<BillDrawerContent billId={billId} />
|
||||
</DrawerSuspense>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withDrawers())(BillDrawer);
|
||||
161
packages/webapp/src/containers/Drawers/BillDrawer/utils.tsx
Normal file
161
packages/webapp/src/containers/Drawers/BillDrawer/utils.tsx
Normal file
@@ -0,0 +1,161 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
MenuItem,
|
||||
Menu,
|
||||
Intent,
|
||||
Tag,
|
||||
} from '@blueprintjs/core';
|
||||
import {
|
||||
FormatNumberCell,
|
||||
TextOverviewTooltipCell,
|
||||
FormattedMessage as T,
|
||||
Choose,
|
||||
Icon,
|
||||
} from '@/components';
|
||||
import { getColumnWidth } from '@/utils';
|
||||
import { useBillDrawerContext } from './BillDrawerProvider';
|
||||
|
||||
/**
|
||||
* Retrieve bill readonly details entries table columns.
|
||||
*/
|
||||
export const useBillReadonlyEntriesTableColumns = () => {
|
||||
const {
|
||||
bill: { entries },
|
||||
} = useBillDrawerContext();
|
||||
|
||||
return React.useMemo(
|
||||
() => [
|
||||
{
|
||||
Header: intl.get('product_and_service'),
|
||||
accessor: 'item.name',
|
||||
Cell: TextOverviewTooltipCell,
|
||||
width: 150,
|
||||
className: 'item',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('description'),
|
||||
accessor: 'description',
|
||||
Cell: TextOverviewTooltipCell,
|
||||
className: 'description',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('quantity'),
|
||||
accessor: 'quantity',
|
||||
Cell: FormatNumberCell,
|
||||
width: getColumnWidth(entries, 'quantity', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('rate'),
|
||||
accessor: 'rate',
|
||||
Cell: FormatNumberCell,
|
||||
width: getColumnWidth(entries, 'rate', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
Header: intl.get('amount'),
|
||||
accessor: 'amount',
|
||||
Cell: FormatNumberCell,
|
||||
width: getColumnWidth(entries, 'amount', {
|
||||
minWidth: 60,
|
||||
magicSpacing: 5,
|
||||
}),
|
||||
align: 'right',
|
||||
disableSortBy: true,
|
||||
textOverview: true,
|
||||
},
|
||||
],
|
||||
[],
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Bill details status.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
export function BillDetailsStatus({ bill }) {
|
||||
return (
|
||||
<Choose>
|
||||
<Choose.When condition={bill.is_fully_paid && bill.is_open}>
|
||||
<StatusTag intent={Intent.SUCCESS} round={true}>
|
||||
<T id={'paid'} />
|
||||
</StatusTag>
|
||||
</Choose.When>
|
||||
|
||||
<Choose.When condition={bill.is_open}>
|
||||
<Choose>
|
||||
<Choose.When condition={bill.is_overdue}>
|
||||
<StatusTag intent={Intent.WARNING} round={true}>
|
||||
<T id={'overdue'} />
|
||||
</StatusTag>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<StatusTag intent={Intent.PRIMARY} round={true}>
|
||||
<T id={'due'} />
|
||||
</StatusTag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
</Choose.When>
|
||||
<Choose.Otherwise>
|
||||
<StatusTag round={true} minimal={true}>
|
||||
<T id={'draft'} />
|
||||
</StatusTag>
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
|
||||
export const BillMenuItem = ({
|
||||
payload: { onConvert, onAllocateLandedCost },
|
||||
}) => {
|
||||
return (
|
||||
<Popover
|
||||
minimal={true}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.BOTTOM_LEFT}
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
content={
|
||||
<Menu>
|
||||
<MenuItem
|
||||
onClick={onAllocateLandedCost}
|
||||
text={<T id={'bill.allocate_landed_coast'} />}
|
||||
/>
|
||||
<MenuItem
|
||||
onClick={onConvert}
|
||||
text={<T id={'bill.convert_to_credit_note'} />}
|
||||
/>
|
||||
</Menu>
|
||||
}
|
||||
>
|
||||
<Button icon={<Icon icon="more-vert" iconSize={16} />} minimal={true} />
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
||||
const StatusTag = styled(Tag)`
|
||||
min-width: 65px;
|
||||
text-align: center;
|
||||
`;
|
||||
Reference in New Issue
Block a user