feat: optimize style of reconcile customer/vendor credit table.

This commit is contained in:
a.bouhuolia
2021-12-21 20:09:32 +02:00
parent dd7b44eb29
commit 1e5b394575
8 changed files with 321 additions and 263 deletions

View File

@@ -1,7 +1,9 @@
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import styled from 'styled-components';
import { MoneyFieldCell, DataTableEditable, FormatDateCell } from 'components';
import { DataTableEditable } from 'components';
import { compose, updateTableCell } from 'utils'; import { compose, updateTableCell } from 'utils';
import { useReconcileCreditNoteTableColumns } from './utils';
/** /**
* Reconcile credit note entries table. * Reconcile credit note entries table.
@@ -11,45 +13,8 @@ export default function ReconcileCreditNoteEntriesTable({
entries, entries,
errors, errors,
}) { }) {
const columns = React.useMemo( // Retrieve the reconcile credit note table columns.
() => [ const columns = useReconcileCreditNoteTableColumns();
{
Header: intl.get('invoice_date'),
accessor: 'formatted_invoice_date',
Cell: FormatDateCell,
disableSortBy: true,
width: '120',
},
{
Header: intl.get('invoice_no'),
accessor: 'invoice_no',
disableSortBy: true,
width: '100',
},
{
Header: intl.get('amount'),
accessor: 'formatted_amount',
disableSortBy: true,
align: 'right',
width: '100',
},
{
Header: intl.get('reconcile_credit_note.column.remaining_amount'),
accessor: 'formatted_due_amount',
disableSortBy: true,
align: 'right',
width: '150',
},
{
Header: intl.get('reconcile_credit_note.column.amount_to_credit'),
accessor: 'amount',
Cell: MoneyFieldCell,
disableSortBy: true,
width: '150',
},
],
[],
);
// Handle update data. // Handle update data.
const handleUpdateData = React.useCallback( const handleUpdateData = React.useCallback(
@@ -63,7 +28,7 @@ export default function ReconcileCreditNoteEntriesTable({
); );
return ( return (
<DataTableEditable <ReconcileCreditNoteEditableTable
columns={columns} columns={columns}
data={entries} data={entries}
payload={{ payload={{
@@ -73,3 +38,22 @@ export default function ReconcileCreditNoteEntriesTable({
/> />
); );
} }
export const ReconcileCreditNoteEditableTable = styled(DataTableEditable)`
.table {
max-height: 400px;
overflow: auto;
.thead .tr .th {
padding-top: 8px;
padding-bottom: 8px;
}
.tbody {
.tr .td {
padding: 2px 4px;
min-height: 38px;
}
}
}
`;

View File

@@ -1,7 +1,15 @@
import React from 'react'; import React from 'react';
import { FastField, useFormikContext } from 'formik'; import { FastField, useFormikContext } from 'formik';
import { Classes } from '@blueprintjs/core'; import { Classes } from '@blueprintjs/core';
import { T, TotalLines, TotalLine } from 'components'; import styled from 'styled-components';
import {
T,
TotalLines,
TotalLine,
TotalLineBorderStyle,
TotalLineTextStyle,
} from 'components';
import { subtract } from 'lodash'; import { subtract } from 'lodash';
import { getEntriesTotal } from 'containers/Entries/utils'; import { getEntriesTotal } from 'containers/Entries/utils';
import ReconcileCreditNoteEntriesTable from './ReconcileCreditNoteEntriesTable'; import ReconcileCreditNoteEntriesTable from './ReconcileCreditNoteEntriesTable';
@@ -13,11 +21,44 @@ import { formattedAmount } from 'utils';
*/ */
export default function ReconcileCreditNoteFormFields() { export default function ReconcileCreditNoteFormFields() {
const { const {
creditNote: { creditNote: { formatted_credits_remaining },
formatted_credits_remaining, } = useReconcileCreditNoteContext();
credits_remaining,
currency_code, return (
}, <div className={Classes.DIALOG_BODY}>
<CreditRemainingRoot>
<T id={'reconcile_credit_note.dialog.credits_balance'} />
<CreditRemainingBalance>
{formatted_credits_remaining}
</CreditRemainingBalance>
</CreditRemainingRoot>
{/*------------ Reconcile credit entries table -----------*/}
<FastField name={'entries'}>
{({ form: { setFieldValue }, field: { value }, meta: { error } }) => (
<ReconcileCreditNoteEntriesTable
entries={value}
errors={error}
onUpdateData={(newEntries) => {
setFieldValue('entries', newEntries);
}}
/>
)}
</FastField>
<ReconcileCreditNoteTotalLines />
</div>
);
}
/**
* Reconcile credit note total lines.
* @returns {React.JSX}
*/
function ReconcileCreditNoteTotalLines() {
const {
creditNote: { credits_remaining, currency_code },
} = useReconcileCreditNoteContext(); } = useReconcileCreditNoteContext();
const { values } = useFormikContext(); const { values } = useFormikContext();
@@ -32,46 +73,42 @@ export default function ReconcileCreditNoteFormFields() {
const creditsRemaining = subtract(credits_remaining, totalAmount); const creditsRemaining = subtract(credits_remaining, totalAmount);
return ( return (
<div className={Classes.DIALOG_BODY}> <ReconcileCreditNoteTotalLinesRoot>
<div className="credit-remaining"> <ReconcileTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
<div className="credit-remaining__label"> <TotalLine
<T id={'reconcile_credit_note.dialog.credits_balance'} /> title={
</div> <T id={'reconcile_credit_note.dialog.total_amount_to_credit'} />
<div className="credit-remaining__balance"> }
{formatted_credits_remaining} value={formattedAmount(totalAmount, currency_code)}
</div> borderStyle={TotalLineBorderStyle.SingleDark}
</div> />
<TotalLine
{/*------------ Reconcile credit entries table -----------*/} title={<T id={'reconcile_credit_note.dialog.remaining_credits'} />}
<FastField name={'entries'}> value={formattedAmount(creditsRemaining, currency_code)}
{({ borderStyle={TotalLineBorderStyle.SingleDark}
form: { setFieldValue, values }, textStyle={TotalLineTextStyle.Bold}
field: { value }, />
meta: { error, touched }, </ReconcileTotalLines>
}) => ( </ReconcileCreditNoteTotalLinesRoot>
<ReconcileCreditNoteEntriesTable
entries={value}
errors={error}
onUpdateData={(newEntries) => {
setFieldValue('entries', newEntries);
}}
/>
)}
</FastField>
<div className="footer">
<TotalLines className="total_lines">
<TotalLine
title={
<T id={'reconcile_credit_note.dialog.total_amount_to_credit'} />
}
value={formattedAmount(totalAmount, currency_code)}
/>
<TotalLine
title={<T id={'reconcile_credit_note.dialog.remaining_credits'} />}
value={formattedAmount(creditsRemaining, currency_code)}
/>
</TotalLines>
</div>
</div>
); );
} }
export const CreditRemainingRoot = styled.div`
display: flex;
justify-content: flex-end;
padding-bottom: 15px;
`;
export const CreditRemainingBalance = styled.span`
font-weight: 600;
color: #343463;
margin-left: 5px;
`;
export const ReconcileCreditNoteTotalLinesRoot = styled.div`
margin-top: 15px;
`;
export const ReconcileTotalLines = styled(TotalLines)`
margin-left: auto;
`;

View File

@@ -1,8 +1,10 @@
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { Callout, Intent, Classes } from '@blueprintjs/core'; import { Callout, Intent, Classes } from '@blueprintjs/core';
import clsx from 'classnames';
import { AppToaster, T } from 'components'; import { CLASSES } from 'common/classes';
import { MoneyFieldCell, FormatDateCell, AppToaster, T } from 'components';
export const transformErrors = (errors, { setErrors }) => { export const transformErrors = (errors, { setErrors }) => {
if (errors.some((e) => e.type === 'INVOICES_HAS_NO_REMAINING_AMOUNT')) { if (errors.some((e) => e.type === 'INVOICES_HAS_NO_REMAINING_AMOUNT')) {
@@ -33,3 +35,50 @@ export function EmptyStatuCallout() {
</div> </div>
); );
} }
/**
* Retrieves reconcile credit note table columns.
* @returns
*/
export const useReconcileCreditNoteTableColumns = () => {
return React.useMemo(
() => [
{
Header: intl.get('invoice_date'),
accessor: 'formatted_invoice_date',
Cell: FormatDateCell,
disableSortBy: true,
width: '120',
},
{
Header: intl.get('invoice_no'),
accessor: 'invoice_no',
disableSortBy: true,
width: '100',
},
{
Header: intl.get('amount'),
accessor: 'formatted_amount',
disableSortBy: true,
align: 'right',
width: '100',
},
{
Header: intl.get('reconcile_credit_note.column.remaining_amount'),
accessor: 'formatted_due_amount',
disableSortBy: true,
align: 'right',
width: '150',
className: clsx(CLASSES.FONT_BOLD),
},
{
Header: intl.get('reconcile_credit_note.column.amount_to_credit'),
accessor: 'amount',
Cell: MoneyFieldCell,
disableSortBy: true,
width: '150',
},
],
[],
)
}

View File

@@ -1,52 +1,17 @@
import React from 'react'; import React from 'react';
import intl from 'react-intl-universal'; import styled from 'styled-components';
import { MoneyFieldCell, DataTableEditable, FormatDateCell } from 'components';
import { DataTableEditable } from 'components';
import { compose, updateTableCell } from 'utils'; import { compose, updateTableCell } from 'utils';
import { useReconcileVendorCreditTableColumns } from './utils';
export default function ReconcileVendorCreditEntriesTable({ export default function ReconcileVendorCreditEntriesTable({
onUpdateData, onUpdateData,
entries, entries,
errors, errors,
}) { }) {
const columns = React.useMemo( // Reconcile vendor credit table columns.
() => [ const columns = useReconcileVendorCreditTableColumns();
{
Header: intl.get('bill_date'),
accessor: 'formatted_bill_date',
Cell: FormatDateCell,
disableSortBy: true,
width: '120',
},
{
Header: intl.get('reconcile_vendor_credit.column.bill_number'),
accessor: 'bill_number',
disableSortBy: true,
width: '100',
},
{
Header: intl.get('amount'),
accessor: 'formatted_amount',
disableSortBy: true,
align: 'right',
width: '100',
},
{
Header: intl.get('reconcile_vendor_credit.column.remaining_amount'),
accessor: 'formatted_due_amount',
disableSortBy: true,
align: 'right',
width: '150',
},
{
Header: intl.get('reconcile_vendor_credit.column.amount_to_credit'),
accessor: 'amount',
Cell: MoneyFieldCell,
disableSortBy: true,
width: '150',
},
],
[],
);
// Handle update data. // Handle update data.
const handleUpdateData = React.useCallback( const handleUpdateData = React.useCallback(
@@ -60,7 +25,7 @@ export default function ReconcileVendorCreditEntriesTable({
); );
return ( return (
<DataTableEditable <ReconcileVendorCreditEditableTable
columns={columns} columns={columns}
data={entries} data={entries}
payload={{ payload={{
@@ -70,3 +35,22 @@ export default function ReconcileVendorCreditEntriesTable({
/> />
); );
} }
export const ReconcileVendorCreditEditableTable = styled(DataTableEditable)`
.table {
max-height: 400px;
overflow: auto;
.thead .tr .th {
padding-top: 8px;
padding-bottom: 8px;
}
.tbody {
.tr .td {
padding: 2px 4px;
min-height: 38px;
}
}
}
`;

View File

@@ -2,42 +2,34 @@ import React from 'react';
import { FastField, useFormikContext } from 'formik'; import { FastField, useFormikContext } from 'formik';
import { Classes } from '@blueprintjs/core'; import { Classes } from '@blueprintjs/core';
import { subtract } from 'lodash'; import { subtract } from 'lodash';
import styled from 'styled-components';
import { getEntriesTotal } from 'containers/Entries/utils'; import { getEntriesTotal } from 'containers/Entries/utils';
import { T, TotalLines, TotalLine } from 'components'; import {
T,
TotalLines,
TotalLine,
TotalLineBorderStyle,
TotalLineTextStyle,
} from 'components';
import ReconcileVendorCreditEntriesTable from './ReconcileVendorCreditEntriesTable'; import ReconcileVendorCreditEntriesTable from './ReconcileVendorCreditEntriesTable';
import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider'; import { useReconcileVendorCreditContext } from './ReconcileVendorCreditFormProvider';
import { formattedAmount } from 'utils'; import { formattedAmount } from 'utils';
export default function ReconcileVendorCreditFormFields() { export default function ReconcileVendorCreditFormFields() {
const { const {
vendorCredit: { vendorCredit: { formatted_credits_remaining },
currency_code,
credits_remaining,
formatted_credits_remaining,
},
} = useReconcileVendorCreditContext(); } = useReconcileVendorCreditContext();
const { values } = useFormikContext();
// Calculate the total amount of credit entries.
const totalAmount = React.useMemo(
() => getEntriesTotal(values.entries),
[values.entries],
);
// Calculate the total amount of credit remaining.
const creditsRemaining = subtract(credits_remaining, totalAmount);
return ( return (
<div className={Classes.DIALOG_BODY}> <div className={Classes.DIALOG_BODY}>
<div className="credit-remaining"> <CreditRemainingRoot>
<div className="credit-remaining__label"> <T id={'reconcile_vendor_note.dialog.credits_balance'} />
<T id={'reconcile_vendor_note.dialog.credits_balance'} />
</div> <CreditRemainingBalance>
<div className="credit-remaining__balance">
{formatted_credits_remaining} {formatted_credits_remaining}
</div> </CreditRemainingBalance>
</div> </CreditRemainingRoot>
<FastField name={'entries'}> <FastField name={'entries'}>
{({ {({
@@ -54,22 +46,68 @@ export default function ReconcileVendorCreditFormFields() {
/> />
)} )}
</FastField> </FastField>
<div className="footer">
<TotalLines className="total_lines"> <ReconcileVendorCreditTotalLines />
<TotalLine
title={
<T id={'reconcile_vendor_credit.dialog.total_amount_to_credit'} />
}
value={formattedAmount(totalAmount, currency_code)}
/>
<TotalLine
title={
<T id={'reconcile_vendor_credit.dialog.remaining_credits'} />
}
value={formattedAmount(creditsRemaining, currency_code)}
/>
</TotalLines>
</div>
</div> </div>
); );
} }
/**
* Reconcile vendor credit total lines.
* @returns {React.JSX}
*/
function ReconcileVendorCreditTotalLines() {
const {
vendorCredit: { currency_code, credits_remaining },
} = useReconcileVendorCreditContext();
const { values } = useFormikContext();
// Calculate the total amount of credit entries.
const totalAmount = React.useMemo(
() => getEntriesTotal(values.entries),
[values.entries],
);
// Calculate the total amount of credit remaining.
const creditsRemaining = subtract(credits_remaining, totalAmount);
return (
<ReconcileVendorCreditTotalLinesRoot>
<ReconcileTotalLines labelColWidth={'180px'} amountColWidth={'180px'}>
<TotalLine
title={
<T id={'reconcile_vendor_credit.dialog.total_amount_to_credit'} />
}
value={formattedAmount(totalAmount, currency_code)}
borderStyle={TotalLineBorderStyle.SingleDark}
/>
<TotalLine
title={<T id={'reconcile_vendor_credit.dialog.remaining_credits'} />}
value={formattedAmount(creditsRemaining, currency_code)}
borderStyle={TotalLineBorderStyle.SingleDark}
textStyle={TotalLineTextStyle.Bold}
/>
</ReconcileTotalLines>
</ReconcileVendorCreditTotalLinesRoot>
);
}
const CreditRemainingRoot = styled.div`
display: flex;
justify-content: flex-end;
padding-bottom: 15px;
`;
const CreditRemainingBalance = styled.span`
font-weight: 600;
color: #343463;
margin-left: 5px;
`;
export const ReconcileVendorCreditTotalLinesRoot = styled.div`
margin-top: 15px;
`;
export const ReconcileTotalLines = styled(TotalLines)`
margin-left: auto;
`;

View File

@@ -1,6 +1,11 @@
import React from 'react'; import React from 'react';
import { Callout, Intent, Classes } from '@blueprintjs/core'; import { Callout, Intent, Classes } from '@blueprintjs/core';
import { AppToaster, T } from 'components'; import intl from 'react-intl-universal';
import clsx from 'classnames';
import { CLASSES } from 'common/classes';
import { T } from 'components';
import { MoneyFieldCell, FormatDateCell } from 'components';
export const transformErrors = (errors, { setErrors }) => {}; export const transformErrors = (errors, { setErrors }) => {};
@@ -15,3 +20,49 @@ export function EmptyStatuCallout() {
</div> </div>
); );
} }
/**
* Reconcile vendor credit table columns.
*/
export const useReconcileVendorCreditTableColumns = () => {
return React.useMemo(
() => [
{
Header: intl.get('bill_date'),
accessor: 'formatted_bill_date',
Cell: FormatDateCell,
disableSortBy: true,
width: '120',
},
{
Header: intl.get('reconcile_vendor_credit.column.bill_number'),
accessor: 'bill_number',
disableSortBy: true,
width: '100',
},
{
Header: intl.get('amount'),
accessor: 'formatted_amount',
disableSortBy: true,
align: 'right',
width: '100',
},
{
Header: intl.get('reconcile_vendor_credit.column.remaining_amount'),
accessor: 'formatted_due_amount',
disableSortBy: true,
align: 'right',
width: '150',
className: clsx(CLASSES.FONT_BOLD),
},
{
Header: intl.get('reconcile_vendor_credit.column.amount_to_credit'),
accessor: 'amount',
Cell: MoneyFieldCell,
disableSortBy: true,
width: '150',
},
],
[],
);
};

View File

@@ -1,52 +1,10 @@
.dialog--reconcile-credit-form { .dialog--reconcile-credit-form {
width: 850px; width: 850px;
.bp3-dialog-body {
.footer {
display: flex;
margin-top: 20px;
.total_lines {
margin-left: auto;
&_line {
border-bottom: none;
.title {
font-weight: 600;
}
.amount,
.title {
padding: 8px 0px;
width: 150px;
}
.amount {
text-align: right;
}
}
}
}
}
.bigcapital-datatable {
}
.credit-remaining {
display: flex;
justify-content: flex-end;
padding-top: 5px;
padding-bottom: 15px;
&__label {
}
&__balance {
font-weight: 500;
color: #343463;
margin-left: 5px;
}
}
.bp3-callout { .bp3-callout {
font-size: 14px; font-size: 14px;
} }
.bp3-dialog-footer { .bp3-dialog-footer {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;

View File

@@ -1,32 +1,6 @@
.dialog--reconcile-vendor-credit-form { .dialog--reconcile-vendor-credit-form {
width: 850px; width: 850px;
.bp3-dialog-body {
.footer {
display: flex;
margin-top: 40px;
.total_lines {
margin-left: auto;
&_line {
border-bottom: none;
.title {
font-weight: 600;
}
.amount,
.title {
padding: 8px 0px;
width: 165px;
}
.amount {
text-align: right;
}
}
}
}
}
.bigcapital-datatable { .bigcapital-datatable {
.table { .table {
border: 1px solid #d1dee2; border: 1px solid #d1dee2;
@@ -36,17 +10,13 @@
.tbody-inner { .tbody-inner {
height: auto; height: auto;
scrollbar-width: none; scrollbar-width: none;
&::-webkit-scrollbar { &::-webkit-scrollbar {
display: none; display: none;
} }
} }
.tbody {
.tr .td {
padding: 0.4rem;
margin-left: -1px;
border-left: 1px solid #ececec;
}
.tbody {
.bp3-form-group { .bp3-form-group {
margin-bottom: 0; margin-bottom: 0;
@@ -67,19 +37,6 @@
font-size: 14px; font-size: 14px;
} }
.credit-remaining {
display: flex;
justify-content: flex-end;
padding-top: 5px;
padding-bottom: 15px;
&__label {
}
&__balance {
font-weight: 500;
color: #343463;
margin-left: 5px;
}
}
.bp3-dialog-footer { .bp3-dialog-footer {
display: flex; display: flex;
justify-content: flex-start; justify-content: flex-start;