mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
Merge branch 'master' of https://github.com/abouolia/Bigcapital
This commit is contained in:
@@ -1,23 +1,21 @@
|
||||
import React from 'react';
|
||||
import { Button } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { saveInvoke, removeRowsByIndex } from 'utils';
|
||||
import { DataTableEditable } from 'components';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { updateDataReducer } from './utils';
|
||||
import {
|
||||
compose,
|
||||
saveInvoke,
|
||||
updateMinEntriesLines,
|
||||
updateRemoveLineByIndex,
|
||||
updateAutoAddNewLine,
|
||||
updateTableRow,
|
||||
} from 'utils';
|
||||
import { useMakeJournalFormContext } from './MakeJournalProvider';
|
||||
import { useJournalTableEntriesColumns } from './components';
|
||||
|
||||
import { compose } from 'redux';
|
||||
import { updateAdjustEntries } from './utils';
|
||||
|
||||
/**
|
||||
* Make journal entries table component.
|
||||
*/
|
||||
function MakeJournalEntriesTable({
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
export default function MakeJournalEntriesTable({
|
||||
// #ownPorps
|
||||
onChange,
|
||||
entries,
|
||||
@@ -30,22 +28,34 @@ function MakeJournalEntriesTable({
|
||||
|
||||
// Memorized data table columns.
|
||||
const columns = useJournalTableEntriesColumns();
|
||||
|
||||
|
||||
// Handles update datatable data.
|
||||
const handleUpdateData = (rowIndex, columnId, value) => {
|
||||
const newRows = updateDataReducer(entries, rowIndex, columnId, value);
|
||||
const newRows = compose(
|
||||
// Auto-adding new lines.
|
||||
updateAutoAddNewLine(defaultEntry, ['account_id', 'credit', 'debit']),
|
||||
// Update items entries total.
|
||||
updateAdjustEntries(rowIndex, columnId, value),
|
||||
// Update entry of the given row index and column id.
|
||||
updateTableRow(rowIndex, columnId, value),
|
||||
)(entries);
|
||||
|
||||
saveInvoke(onChange, newRows);
|
||||
};
|
||||
|
||||
// Handle remove datatable row.
|
||||
const handleRemoveRow = (rowIndex) => {
|
||||
const newRows = removeRowsByIndex(entries, rowIndex);
|
||||
const newRows = compose(
|
||||
// Ensure minimum lines count.
|
||||
updateMinEntriesLines(minLinesNumber, defaultEntry),
|
||||
// Remove the line by the given index.
|
||||
updateRemoveLineByIndex(rowIndex),
|
||||
)(entries);
|
||||
|
||||
saveInvoke(onChange, newRows);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
|
||||
return (
|
||||
<DataTableEditable
|
||||
columns={columns}
|
||||
data={entries}
|
||||
@@ -63,8 +73,6 @@ function MakeJournalEntriesTable({
|
||||
})),
|
||||
autoFocus: ['account_id', 0],
|
||||
}}
|
||||
/>
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertActions)(MakeJournalEntriesTable);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { Intent } from '@blueprintjs/core';
|
||||
import { sumBy, setWith, toSafeInteger, get } from 'lodash';
|
||||
import moment from 'moment';
|
||||
|
||||
import { transformUpdatedRows, repeatValue, transformToForm } from 'utils';
|
||||
import { updateTableRow, repeatValue, transformToForm } from 'utils';
|
||||
import { AppToaster } from 'components';
|
||||
import { formatMessage } from 'services/intl';
|
||||
|
||||
@@ -70,10 +70,14 @@ function adjustmentEntries(entries) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Adjustment credit/debit entries.
|
||||
* @param {number} rowIndex
|
||||
* @param {number} columnId
|
||||
* @param {string} value
|
||||
* @return {array}
|
||||
*/
|
||||
export const updateDataReducer = (rows, rowIndex, columnId, value) => {
|
||||
let newRows = transformUpdatedRows(rows, rowIndex, columnId, value);
|
||||
export const updateAdjustEntries = (rowIndex, columnId, value) => (rows) => {
|
||||
let newRows = [...rows];
|
||||
|
||||
const oldCredit = get(rows, `[${rowIndex}].credit`);
|
||||
const oldDebit = get(rows, `[${rowIndex}].debit`);
|
||||
@@ -82,20 +86,10 @@ export const updateDataReducer = (rows, rowIndex, columnId, value) => {
|
||||
const adjustment = adjustmentEntries(rows);
|
||||
|
||||
if (adjustment.credit) {
|
||||
newRows = transformUpdatedRows(
|
||||
newRows,
|
||||
rowIndex,
|
||||
'credit',
|
||||
adjustment.credit,
|
||||
);
|
||||
newRows = updateTableRow(rowIndex, 'credit', adjustment.credit)(newRows);
|
||||
}
|
||||
if (adjustment.debit) {
|
||||
newRows = transformUpdatedRows(
|
||||
newRows,
|
||||
rowIndex,
|
||||
'debit',
|
||||
adjustment.debit,
|
||||
);
|
||||
newRows = updateTableRow(rowIndex, 'debit', adjustment.debit)(newRows);
|
||||
}
|
||||
}
|
||||
return newRows;
|
||||
|
||||
@@ -17,6 +17,17 @@ export default function InviteUserFormContent() {
|
||||
// Formik context.
|
||||
const { isSubmitting } = useFormikContext();
|
||||
|
||||
const [passwordType, setPasswordType] = React.useState('password');
|
||||
|
||||
// Handle password revealer changing.
|
||||
const handlePasswordRevealerChange = React.useCallback(
|
||||
(shown) => {
|
||||
const type = shown ? 'text' : 'password';
|
||||
setPasswordType(type);
|
||||
},
|
||||
[setPasswordType],
|
||||
);
|
||||
|
||||
return (
|
||||
<Form>
|
||||
<Row>
|
||||
@@ -74,14 +85,14 @@ export default function InviteUserFormContent() {
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={<PasswordRevealer />}
|
||||
labelInfo={<PasswordRevealer onChange={handlePasswordRevealerChange} />}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
// type={shown ? 'text' : 'password'}
|
||||
type={passwordType}
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
FormGroup,
|
||||
Checkbox,
|
||||
} from '@blueprintjs/core';
|
||||
import { Form, ErrorMessage, FastField } from 'formik';
|
||||
import { Form, ErrorMessage, Field } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { inputIntent } from 'utils';
|
||||
import { PasswordRevealer } from './components';
|
||||
@@ -14,12 +14,21 @@ import { PasswordRevealer } from './components';
|
||||
/**
|
||||
* Login form.
|
||||
*/
|
||||
export default function LoginForm({
|
||||
isSubmitting
|
||||
}) {
|
||||
export default function LoginForm({ isSubmitting }) {
|
||||
const [passwordType, setPasswordType] = React.useState('password');
|
||||
|
||||
// Handle password revealer changing.
|
||||
const handlePasswordRevealerChange = React.useCallback(
|
||||
(shown) => {
|
||||
const type = shown ? 'text' : 'password';
|
||||
setPasswordType(type);
|
||||
},
|
||||
[setPasswordType],
|
||||
);
|
||||
|
||||
return (
|
||||
<Form className={'authentication-page__form'}>
|
||||
<FastField name={'crediential'}>
|
||||
<Field name={'crediential'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'email_or_phone_number'} />}
|
||||
@@ -34,13 +43,15 @@ export default function LoginForm({
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Field>
|
||||
|
||||
<FastField name={'password'}>
|
||||
<Field name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
labelInfo={<PasswordRevealer />}
|
||||
labelInfo={
|
||||
<PasswordRevealer onChange={handlePasswordRevealerChange} />
|
||||
}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
@@ -48,12 +59,12 @@ export default function LoginForm({
|
||||
<InputGroup
|
||||
large={true}
|
||||
intent={inputIntent({ error, touched })}
|
||||
// type={shown ? 'text' : 'password'}
|
||||
type={passwordType}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Field>
|
||||
|
||||
<div className={'login-form__checkbox-section'}>
|
||||
<Checkbox large={true} className={'checkbox--remember-me'}>
|
||||
|
||||
@@ -4,26 +4,35 @@ import {
|
||||
InputGroup,
|
||||
Intent,
|
||||
FormGroup,
|
||||
Spinner
|
||||
Spinner,
|
||||
} from '@blueprintjs/core';
|
||||
import { ErrorMessage, FastField, Form } from 'formik';
|
||||
import { ErrorMessage, Field, Form } from 'formik';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Row, Col, If } from 'components';
|
||||
|
||||
import { PasswordRevealer } from './components';
|
||||
import { inputIntent } from 'utils';
|
||||
|
||||
/**
|
||||
* Register form.
|
||||
*/
|
||||
export default function RegisterForm({
|
||||
isSubmitting,
|
||||
}) {
|
||||
export default function RegisterForm({ isSubmitting }) {
|
||||
const [passwordType, setPasswordType] = React.useState('password');
|
||||
|
||||
// Handle password revealer changing.
|
||||
const handlePasswordRevealerChange = React.useCallback(
|
||||
(shown) => {
|
||||
const type = shown ? 'text' : 'password';
|
||||
setPasswordType(type);
|
||||
},
|
||||
[setPasswordType],
|
||||
);
|
||||
|
||||
return (
|
||||
<Form className={'authentication-page__form'}>
|
||||
<Row className={'name-section'}>
|
||||
<Col md={6}>
|
||||
<FastField name={'first_name'}>
|
||||
<Field name={'first_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'first_name'} />}
|
||||
@@ -37,11 +46,11 @@ export default function RegisterForm({
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Field>
|
||||
</Col>
|
||||
|
||||
<Col md={6}>
|
||||
<FastField name={'last_name'}>
|
||||
<Field name={'last_name'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'last_name'} />}
|
||||
@@ -55,11 +64,11 @@ export default function RegisterForm({
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Field>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FastField name={'phone_number'}>
|
||||
<Field name={'phone_number'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'phone_number'} />}
|
||||
@@ -70,8 +79,9 @@ export default function RegisterForm({
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
<FastField name={'email'}>
|
||||
</Field>
|
||||
|
||||
<Field name={'email'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'email'} />}
|
||||
@@ -82,28 +92,28 @@ export default function RegisterForm({
|
||||
<InputGroup intent={inputIntent({ error, touched })} {...field} />
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Field>
|
||||
|
||||
<FastField name={'password'}>
|
||||
<Field name={'password'}>
|
||||
{({ form, field, meta: { error, touched } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'password'} />}
|
||||
// labelInfo={passwordRevealerTmp}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={
|
||||
<ErrorMessage name={'password'} />
|
||||
labelInfo={
|
||||
<PasswordRevealer onChange={handlePasswordRevealerChange} />
|
||||
}
|
||||
intent={inputIntent({ error, touched })}
|
||||
helperText={<ErrorMessage name={'password'} />}
|
||||
className={'form-group--password has-password-revealer'}
|
||||
>
|
||||
<InputGroup
|
||||
lang={true}
|
||||
// type={shown ? 'text' : 'password'}
|
||||
type={passwordType}
|
||||
intent={inputIntent({ error, touched })}
|
||||
{...field}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
</FastField>
|
||||
</Field>
|
||||
|
||||
<div className={'register-form__agreement-section'}>
|
||||
<p>
|
||||
|
||||
@@ -2,10 +2,18 @@ import React from 'react';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import ContentLoader from 'react-content-loader';
|
||||
import { If, Icon } from 'components';
|
||||
import { saveInvoke } from 'utils';
|
||||
|
||||
export function PasswordRevealer({ defaultShown = false, onChange }) {
|
||||
const [shown, setShown] = React.useState(defaultShown);
|
||||
|
||||
const handleClick = () => {
|
||||
setShown(!shown);
|
||||
saveInvoke(onChange, !shown);
|
||||
};
|
||||
|
||||
export function PasswordRevealer({ shown, onClick }) {
|
||||
return (
|
||||
<span class="password-revealer" onClick={onClick}>
|
||||
<span class="password-revealer" onClick={handleClick}>
|
||||
<If condition={shown}>
|
||||
<Icon icon="eye-slash" />{' '}
|
||||
<span class="text">
|
||||
|
||||
@@ -1,40 +1,25 @@
|
||||
import React, { useEffect, useCallback } from 'react';
|
||||
import { Button } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { useItem } from 'hooks/query';
|
||||
|
||||
import ItemsEntriesDeleteAlert from 'containers/Alerts/ItemsEntries/ItemsEntriesDeleteAlert';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { DataTableEditable } from 'components';
|
||||
|
||||
import { useEditableItemsEntriesColumns } from './components';
|
||||
import {
|
||||
saveInvoke,
|
||||
updateTableRow,
|
||||
repeatValue,
|
||||
removeRowsByIndex,
|
||||
compose,
|
||||
updateTableRow,
|
||||
updateMinEntriesLines,
|
||||
updateAutoAddNewLine,
|
||||
updateRemoveLineByIndex,
|
||||
} from 'utils';
|
||||
import { updateItemsEntriesTotal, ITEM_TYPE } from './utils';
|
||||
import { last } from 'lodash';
|
||||
|
||||
const updateAutoAddNewLine = (defaultEntry) => (entries) => {
|
||||
const newEntries = [...entries];
|
||||
const lastEntry = last(newEntries);
|
||||
|
||||
return (lastEntry.item_id) ? [...entries, defaultEntry] : [...entries];
|
||||
};
|
||||
|
||||
/**
|
||||
* Items entries table.
|
||||
*/
|
||||
function ItemsEntriesTable({
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #ownProps
|
||||
items,
|
||||
entries,
|
||||
@@ -116,7 +101,7 @@ function ItemsEntriesTable({
|
||||
setRowItem({ rowIndex, columnId, itemId: value });
|
||||
}
|
||||
const newRows = compose(
|
||||
updateAutoAddNewLine(defaultEntry),
|
||||
updateAutoAddNewLine(defaultEntry, ['item_id']),
|
||||
updateItemsEntriesTotal,
|
||||
updateTableRow(rowIndex, columnId, value),
|
||||
)(rows);
|
||||
@@ -129,55 +114,35 @@ function ItemsEntriesTable({
|
||||
|
||||
// Handle table rows removing by index.
|
||||
const handleRemoveRow = (rowIndex) => {
|
||||
const newRows = removeRowsByIndex(rows, rowIndex);
|
||||
setRows(newRows);
|
||||
saveInvoke(onUpdateData, newRows);
|
||||
};
|
||||
const newRows = compose(
|
||||
// Ensure minimum lines count.
|
||||
updateMinEntriesLines(4, defaultEntry),
|
||||
// Remove the line by the given index.
|
||||
updateRemoveLineByIndex(rowIndex),
|
||||
)(rows);
|
||||
|
||||
// Handle table rows adding a new row.
|
||||
const onClickNewRow = (event) => {
|
||||
const newRows = [...rows, defaultEntry];
|
||||
setRows(newRows);
|
||||
saveInvoke(onUpdateData, newRows);
|
||||
};
|
||||
|
||||
// Handle table clearing all rows.
|
||||
const handleClickClearAllLines = (event) => {
|
||||
openAlert('items-entries-clear-lines');
|
||||
};
|
||||
|
||||
// Handle alert confirm of clear all lines.
|
||||
const handleClearLinesAlertConfirm = () => {
|
||||
const newRows = repeatValue(defaultEntry, linesNumber);
|
||||
setRows(newRows);
|
||||
saveInvoke(onUpdateData, newRows);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTableEditable
|
||||
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
|
||||
columns={columns}
|
||||
data={rows}
|
||||
sticky={true}
|
||||
progressBarLoading={isItemFetching}
|
||||
cellsLoading={isItemFetching}
|
||||
cellsLoadingCoords={cellsLoading}
|
||||
footer={true}
|
||||
payload={{
|
||||
items,
|
||||
errors: errors || [],
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
autoFocus: ['item_id', 0],
|
||||
}}
|
||||
|
||||
/>
|
||||
<ItemsEntriesDeleteAlert
|
||||
name={'items-entries-clear-lines'}
|
||||
onConfirm={handleClearLinesAlertConfirm}
|
||||
/>
|
||||
</>
|
||||
<DataTableEditable
|
||||
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
|
||||
columns={columns}
|
||||
data={rows}
|
||||
sticky={true}
|
||||
progressBarLoading={isItemFetching}
|
||||
cellsLoading={isItemFetching}
|
||||
cellsLoadingCoords={cellsLoading}
|
||||
footer={true}
|
||||
payload={{
|
||||
items,
|
||||
errors: errors || [],
|
||||
updateData: handleUpdateData,
|
||||
removeRow: handleRemoveRow,
|
||||
autoFocus: ['item_id', 0],
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -186,7 +151,7 @@ ItemsEntriesTable.defaultProps = {
|
||||
index: 0,
|
||||
item_id: '',
|
||||
description: '',
|
||||
quantity: 1,
|
||||
quantity: '',
|
||||
rate: '',
|
||||
discount: '',
|
||||
},
|
||||
@@ -194,4 +159,4 @@ ItemsEntriesTable.defaultProps = {
|
||||
linesNumber: 4,
|
||||
};
|
||||
|
||||
export default compose(withAlertActions)(ItemsEntriesTable);
|
||||
export default ItemsEntriesTable;
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { repeat } from 'lodash';
|
||||
import { toSafeNumber } from 'utils';
|
||||
|
||||
/**
|
||||
* Retrieve item entry total from the given rate, quantity and discount.
|
||||
* @param {number} rate
|
||||
* @param {number} quantity
|
||||
* @param {number} discount
|
||||
* @param {number} rate
|
||||
* @param {number} quantity
|
||||
* @param {number} discount
|
||||
* @return {number}
|
||||
*/
|
||||
export const calcItemEntryTotal = (discount, quantity, rate) => {
|
||||
@@ -21,9 +22,9 @@ export const calcItemEntryTotal = (discount, quantity, rate) => {
|
||||
export function updateItemsEntriesTotal(rows) {
|
||||
return rows.map((row) => ({
|
||||
...row,
|
||||
total: calcItemEntryTotal(row.discount, row.quantity, row.rate)
|
||||
total: calcItemEntryTotal(row.discount, row.quantity, row.rate),
|
||||
}));
|
||||
};
|
||||
}
|
||||
|
||||
export const ITEM_TYPE = {
|
||||
SELLABLE: 'SELLABLE',
|
||||
|
||||
@@ -3,12 +3,10 @@ import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import ExpenseFormEntriesField from './ExpenseFormEntriesField';
|
||||
|
||||
export default function ExpenseFormBody({
|
||||
|
||||
}) {
|
||||
export default function ExpenseFormBody() {
|
||||
return (
|
||||
<div className={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||
<ExpenseFormEntriesField />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FastField } from 'formik';
|
||||
import React from 'react';
|
||||
import ExpenseFormEntriesTable from './ExpenseFormEntriesTable';
|
||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
import { defaultExpenseEntry } from './utils';
|
||||
|
||||
/**
|
||||
* Expense form entries field.
|
||||
@@ -9,8 +9,6 @@ import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
export default function ExpenseFormEntriesField({
|
||||
linesNumber = 4,
|
||||
}) {
|
||||
const { defaultCategoryEntry } = useExpenseFormContext();
|
||||
|
||||
return (
|
||||
<FastField name={'categories'}>
|
||||
{({ form, field: { value }, meta: { error, touched } }) => (
|
||||
@@ -20,7 +18,7 @@ export default function ExpenseFormEntriesField({
|
||||
onChange={(entries) => {
|
||||
form.setFieldValue('categories', entries);
|
||||
}}
|
||||
defaultEntry={defaultCategoryEntry}
|
||||
defaultEntry={defaultExpenseEntry}
|
||||
linesNumber={linesNumber}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -1,23 +1,21 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { Button } from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import { DataTableEditable } from 'components';
|
||||
import ExpenseDeleteEntriesAlert from 'containers/Alerts/Expenses/ExpenseDeleteEntriesAlert';
|
||||
import { useExpenseFormContext } from './ExpenseFormPageProvider';
|
||||
import { useExpenseFormTableColumns } from './components';
|
||||
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
|
||||
import { transformUpdatedRows, compose, saveInvoke, repeatValue } from 'utils';
|
||||
import {
|
||||
saveInvoke,
|
||||
compose,
|
||||
updateTableRow,
|
||||
updateMinEntriesLines,
|
||||
updateAutoAddNewLine,
|
||||
updateRemoveLineByIndex,
|
||||
} from 'utils';
|
||||
|
||||
/**
|
||||
* Expenses form entries.
|
||||
*/
|
||||
function ExpenseFormEntriesTable({
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
export default function ExpenseFormEntriesTable({
|
||||
// #ownPorps
|
||||
entries,
|
||||
defaultEntry,
|
||||
@@ -32,29 +30,30 @@ function ExpenseFormEntriesTable({
|
||||
|
||||
// Handles update datatable data.
|
||||
const handleUpdateData = useCallback(
|
||||
(rowIndex, columnIdOrObj, value) => {
|
||||
const newRows = transformUpdatedRows(
|
||||
entries,
|
||||
rowIndex,
|
||||
columnIdOrObj,
|
||||
value,
|
||||
);
|
||||
(rowIndex, columnId, value) => {
|
||||
const newRows = compose(
|
||||
updateAutoAddNewLine(defaultEntry, ['expense_account_id']),
|
||||
updateTableRow(rowIndex, columnId, value),
|
||||
)(entries);
|
||||
|
||||
saveInvoke(onChange, newRows);
|
||||
},
|
||||
[entries, onChange],
|
||||
[entries, defaultEntry, onChange],
|
||||
);
|
||||
|
||||
// Handles click remove datatable row.
|
||||
const handleRemoveRow = useCallback(
|
||||
(rowIndex) => {
|
||||
// Can't continue if there is just one row line or less.
|
||||
if (entries.length <= 1) {
|
||||
return;
|
||||
}
|
||||
const newRows = entries.filter((row, index) => index !== rowIndex);
|
||||
const newRows = compose(
|
||||
// Ensure minimum lines count.
|
||||
updateMinEntriesLines(4, defaultEntry),
|
||||
// Remove the line by the given index.
|
||||
updateRemoveLineByIndex(rowIndex),
|
||||
)(entries);
|
||||
|
||||
saveInvoke(onChange, newRows);
|
||||
},
|
||||
[entries, onChange],
|
||||
[entries, defaultEntry, onChange],
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -72,6 +71,4 @@ function ExpenseFormEntriesTable({
|
||||
footer={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(withAlertActions)(ExpenseFormEntriesTable);
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { FastField } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
Reference in New Issue
Block a user