This commit is contained in:
elforjani3
2021-03-08 16:20:01 +02:00
14 changed files with 220 additions and 213 deletions

View File

@@ -1,23 +1,21 @@
import React from 'react'; 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 { DataTableEditable } from 'components';
import withAlertActions from 'containers/Alert/withAlertActions'; import {
compose,
import { updateDataReducer } from './utils'; saveInvoke,
updateMinEntriesLines,
updateRemoveLineByIndex,
updateAutoAddNewLine,
updateTableRow,
} from 'utils';
import { useMakeJournalFormContext } from './MakeJournalProvider'; import { useMakeJournalFormContext } from './MakeJournalProvider';
import { useJournalTableEntriesColumns } from './components'; import { useJournalTableEntriesColumns } from './components';
import { updateAdjustEntries } from './utils';
import { compose } from 'redux';
/** /**
* Make journal entries table component. * Make journal entries table component.
*/ */
function MakeJournalEntriesTable({ export default function MakeJournalEntriesTable({
// #withAlertsActions
openAlert,
// #ownPorps // #ownPorps
onChange, onChange,
entries, entries,
@@ -30,22 +28,34 @@ function MakeJournalEntriesTable({
// Memorized data table columns. // Memorized data table columns.
const columns = useJournalTableEntriesColumns(); const columns = useJournalTableEntriesColumns();
// Handles update datatable data. // Handles update datatable data.
const handleUpdateData = (rowIndex, columnId, value) => { 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); saveInvoke(onChange, newRows);
}; };
// Handle remove datatable row. // Handle remove datatable row.
const handleRemoveRow = (rowIndex) => { 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); saveInvoke(onChange, newRows);
}; };
return (
return (
<DataTableEditable <DataTableEditable
columns={columns} columns={columns}
data={entries} data={entries}
@@ -63,8 +73,6 @@ function MakeJournalEntriesTable({
})), })),
autoFocus: ['account_id', 0], autoFocus: ['account_id', 0],
}} }}
/> />
); );
} }
export default compose(withAlertActions)(MakeJournalEntriesTable);

View File

@@ -3,7 +3,7 @@ import { Intent } from '@blueprintjs/core';
import { sumBy, setWith, toSafeInteger, get } from 'lodash'; import { sumBy, setWith, toSafeInteger, get } from 'lodash';
import moment from 'moment'; import moment from 'moment';
import { transformUpdatedRows, repeatValue, transformToForm } from 'utils'; import { updateTableRow, repeatValue, transformToForm } from 'utils';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { formatMessage } from 'services/intl'; 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) => { export const updateAdjustEntries = (rowIndex, columnId, value) => (rows) => {
let newRows = transformUpdatedRows(rows, rowIndex, columnId, value); let newRows = [...rows];
const oldCredit = get(rows, `[${rowIndex}].credit`); const oldCredit = get(rows, `[${rowIndex}].credit`);
const oldDebit = get(rows, `[${rowIndex}].debit`); const oldDebit = get(rows, `[${rowIndex}].debit`);
@@ -82,20 +86,10 @@ export const updateDataReducer = (rows, rowIndex, columnId, value) => {
const adjustment = adjustmentEntries(rows); const adjustment = adjustmentEntries(rows);
if (adjustment.credit) { if (adjustment.credit) {
newRows = transformUpdatedRows( newRows = updateTableRow(rowIndex, 'credit', adjustment.credit)(newRows);
newRows,
rowIndex,
'credit',
adjustment.credit,
);
} }
if (adjustment.debit) { if (adjustment.debit) {
newRows = transformUpdatedRows( newRows = updateTableRow(rowIndex, 'debit', adjustment.debit)(newRows);
newRows,
rowIndex,
'debit',
adjustment.debit,
);
} }
} }
return newRows; return newRows;

View File

@@ -17,6 +17,17 @@ export default function InviteUserFormContent() {
// Formik context. // Formik context.
const { isSubmitting } = useFormikContext(); 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 ( return (
<Form> <Form>
<Row> <Row>
@@ -74,14 +85,14 @@ export default function InviteUserFormContent() {
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'password'} />} label={<T id={'password'} />}
labelInfo={<PasswordRevealer />} labelInfo={<PasswordRevealer onChange={handlePasswordRevealerChange} />}
className={'form-group--password has-password-revealer'} className={'form-group--password has-password-revealer'}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'password'} />} helperText={<ErrorMessage name={'password'} />}
> >
<InputGroup <InputGroup
lang={true} lang={true}
// type={shown ? 'text' : 'password'} type={passwordType}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
{...field} {...field}
/> />

View File

@@ -6,7 +6,7 @@ import {
FormGroup, FormGroup,
Checkbox, Checkbox,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { Form, ErrorMessage, FastField } from 'formik'; import { Form, ErrorMessage, Field } from 'formik';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { inputIntent } from 'utils'; import { inputIntent } from 'utils';
import { PasswordRevealer } from './components'; import { PasswordRevealer } from './components';
@@ -14,12 +14,21 @@ import { PasswordRevealer } from './components';
/** /**
* Login form. * Login form.
*/ */
export default function LoginForm({ export default function LoginForm({ isSubmitting }) {
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 ( return (
<Form className={'authentication-page__form'}> <Form className={'authentication-page__form'}>
<FastField name={'crediential'}> <Field name={'crediential'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'email_or_phone_number'} />} label={<T id={'email_or_phone_number'} />}
@@ -34,13 +43,15 @@ export default function LoginForm({
/> />
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
<FastField name={'password'}> <Field name={'password'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'password'} />} label={<T id={'password'} />}
labelInfo={<PasswordRevealer />} labelInfo={
<PasswordRevealer onChange={handlePasswordRevealerChange} />
}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'password'} />} helperText={<ErrorMessage name={'password'} />}
className={'form-group--password has-password-revealer'} className={'form-group--password has-password-revealer'}
@@ -48,12 +59,12 @@ export default function LoginForm({
<InputGroup <InputGroup
large={true} large={true}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
// type={shown ? 'text' : 'password'} type={passwordType}
{...field} {...field}
/> />
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
<div className={'login-form__checkbox-section'}> <div className={'login-form__checkbox-section'}>
<Checkbox large={true} className={'checkbox--remember-me'}> <Checkbox large={true} className={'checkbox--remember-me'}>

View File

@@ -4,26 +4,35 @@ import {
InputGroup, InputGroup,
Intent, Intent,
FormGroup, FormGroup,
Spinner Spinner,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { ErrorMessage, FastField, Form } from 'formik'; import { ErrorMessage, Field, Form } from 'formik';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { Row, Col, If } from 'components'; import { Row, Col, If } from 'components';
import { PasswordRevealer } from './components';
import { inputIntent } from 'utils'; import { inputIntent } from 'utils';
/** /**
* Register form. * Register form.
*/ */
export default function RegisterForm({ export default function RegisterForm({ isSubmitting }) {
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 ( return (
<Form className={'authentication-page__form'}> <Form className={'authentication-page__form'}>
<Row className={'name-section'}> <Row className={'name-section'}>
<Col md={6}> <Col md={6}>
<FastField name={'first_name'}> <Field name={'first_name'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'first_name'} />} label={<T id={'first_name'} />}
@@ -37,11 +46,11 @@ export default function RegisterForm({
/> />
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
</Col> </Col>
<Col md={6}> <Col md={6}>
<FastField name={'last_name'}> <Field name={'last_name'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'last_name'} />} label={<T id={'last_name'} />}
@@ -55,11 +64,11 @@ export default function RegisterForm({
/> />
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
</Col> </Col>
</Row> </Row>
<FastField name={'phone_number'}> <Field name={'phone_number'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'phone_number'} />} label={<T id={'phone_number'} />}
@@ -70,8 +79,9 @@ export default function RegisterForm({
<InputGroup intent={inputIntent({ error, touched })} {...field} /> <InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
<FastField name={'email'}>
<Field name={'email'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'email'} />} label={<T id={'email'} />}
@@ -82,28 +92,28 @@ export default function RegisterForm({
<InputGroup intent={inputIntent({ error, touched })} {...field} /> <InputGroup intent={inputIntent({ error, touched })} {...field} />
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
<FastField name={'password'}> <Field name={'password'}>
{({ form, field, meta: { error, touched } }) => ( {({ form, field, meta: { error, touched } }) => (
<FormGroup <FormGroup
label={<T id={'password'} />} label={<T id={'password'} />}
// labelInfo={passwordRevealerTmp} labelInfo={
intent={inputIntent({ error, touched })} <PasswordRevealer onChange={handlePasswordRevealerChange} />
helperText={
<ErrorMessage name={'password'} />
} }
intent={inputIntent({ error, touched })}
helperText={<ErrorMessage name={'password'} />}
className={'form-group--password has-password-revealer'} className={'form-group--password has-password-revealer'}
> >
<InputGroup <InputGroup
lang={true} lang={true}
// type={shown ? 'text' : 'password'} type={passwordType}
intent={inputIntent({ error, touched })} intent={inputIntent({ error, touched })}
{...field} {...field}
/> />
</FormGroup> </FormGroup>
)} )}
</FastField> </Field>
<div className={'register-form__agreement-section'}> <div className={'register-form__agreement-section'}>
<p> <p>

View File

@@ -2,10 +2,18 @@ import React from 'react';
import { FormattedMessage as T } from 'react-intl'; import { FormattedMessage as T } from 'react-intl';
import ContentLoader from 'react-content-loader'; import ContentLoader from 'react-content-loader';
import { If, Icon } from 'components'; 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 ( return (
<span class="password-revealer" onClick={onClick}> <span class="password-revealer" onClick={handleClick}>
<If condition={shown}> <If condition={shown}>
<Icon icon="eye-slash" />{' '} <Icon icon="eye-slash" />{' '}
<span class="text"> <span class="text">

View File

@@ -1,40 +1,25 @@
import React, { useEffect, useCallback } from 'react'; import React, { useEffect, useCallback } from 'react';
import { Button } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import { useItem } from 'hooks/query'; import { useItem } from 'hooks/query';
import ItemsEntriesDeleteAlert from 'containers/Alerts/ItemsEntries/ItemsEntriesDeleteAlert';
import withAlertActions from 'containers/Alert/withAlertActions';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import { DataTableEditable } from 'components'; import { DataTableEditable } from 'components';
import { useEditableItemsEntriesColumns } from './components'; import { useEditableItemsEntriesColumns } from './components';
import { import {
saveInvoke, saveInvoke,
updateTableRow,
repeatValue,
removeRowsByIndex,
compose, compose,
updateTableRow,
updateMinEntriesLines,
updateAutoAddNewLine,
updateRemoveLineByIndex,
} from 'utils'; } from 'utils';
import { updateItemsEntriesTotal, ITEM_TYPE } 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. * Items entries table.
*/ */
function ItemsEntriesTable({ function ItemsEntriesTable({
// #withAlertActions
openAlert,
// #ownProps // #ownProps
items, items,
entries, entries,
@@ -116,7 +101,7 @@ function ItemsEntriesTable({
setRowItem({ rowIndex, columnId, itemId: value }); setRowItem({ rowIndex, columnId, itemId: value });
} }
const newRows = compose( const newRows = compose(
updateAutoAddNewLine(defaultEntry), updateAutoAddNewLine(defaultEntry, ['item_id']),
updateItemsEntriesTotal, updateItemsEntriesTotal,
updateTableRow(rowIndex, columnId, value), updateTableRow(rowIndex, columnId, value),
)(rows); )(rows);
@@ -129,55 +114,35 @@ function ItemsEntriesTable({
// Handle table rows removing by index. // Handle table rows removing by index.
const handleRemoveRow = (rowIndex) => { const handleRemoveRow = (rowIndex) => {
const newRows = removeRowsByIndex(rows, rowIndex); const newRows = compose(
setRows(newRows); // Ensure minimum lines count.
saveInvoke(onUpdateData, newRows); 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); setRows(newRows);
saveInvoke(onUpdateData, newRows); saveInvoke(onUpdateData, newRows);
}; };
return ( return (
<> <DataTableEditable
<DataTableEditable className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)}
className={classNames(CLASSES.DATATABLE_EDITOR_ITEMS_ENTRIES)} columns={columns}
columns={columns} data={rows}
data={rows} sticky={true}
sticky={true} progressBarLoading={isItemFetching}
progressBarLoading={isItemFetching} cellsLoading={isItemFetching}
cellsLoading={isItemFetching} cellsLoadingCoords={cellsLoading}
cellsLoadingCoords={cellsLoading} footer={true}
footer={true} payload={{
payload={{ items,
items, errors: errors || [],
errors: errors || [], updateData: handleUpdateData,
updateData: handleUpdateData, removeRow: handleRemoveRow,
removeRow: handleRemoveRow, autoFocus: ['item_id', 0],
autoFocus: ['item_id', 0], }}
}} />
/>
<ItemsEntriesDeleteAlert
name={'items-entries-clear-lines'}
onConfirm={handleClearLinesAlertConfirm}
/>
</>
); );
} }
@@ -186,7 +151,7 @@ ItemsEntriesTable.defaultProps = {
index: 0, index: 0,
item_id: '', item_id: '',
description: '', description: '',
quantity: 1, quantity: '',
rate: '', rate: '',
discount: '', discount: '',
}, },
@@ -194,4 +159,4 @@ ItemsEntriesTable.defaultProps = {
linesNumber: 4, linesNumber: 4,
}; };
export default compose(withAlertActions)(ItemsEntriesTable); export default ItemsEntriesTable;

View File

@@ -1,10 +1,11 @@
import { repeat } from 'lodash';
import { toSafeNumber } from 'utils'; import { toSafeNumber } from 'utils';
/** /**
* Retrieve item entry total from the given rate, quantity and discount. * Retrieve item entry total from the given rate, quantity and discount.
* @param {number} rate * @param {number} rate
* @param {number} quantity * @param {number} quantity
* @param {number} discount * @param {number} discount
* @return {number} * @return {number}
*/ */
export const calcItemEntryTotal = (discount, quantity, rate) => { export const calcItemEntryTotal = (discount, quantity, rate) => {
@@ -21,9 +22,9 @@ export const calcItemEntryTotal = (discount, quantity, rate) => {
export function updateItemsEntriesTotal(rows) { export function updateItemsEntriesTotal(rows) {
return rows.map((row) => ({ return rows.map((row) => ({
...row, ...row,
total: calcItemEntryTotal(row.discount, row.quantity, row.rate) total: calcItemEntryTotal(row.discount, row.quantity, row.rate),
})); }));
}; }
export const ITEM_TYPE = { export const ITEM_TYPE = {
SELLABLE: 'SELLABLE', SELLABLE: 'SELLABLE',

View File

@@ -3,12 +3,10 @@ import classNames from 'classnames';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import ExpenseFormEntriesField from './ExpenseFormEntriesField'; import ExpenseFormEntriesField from './ExpenseFormEntriesField';
export default function ExpenseFormBody({ export default function ExpenseFormBody() {
}) {
return ( return (
<div className={classNames(CLASSES.PAGE_FORM_BODY)}> <div className={classNames(CLASSES.PAGE_FORM_BODY)}>
<ExpenseFormEntriesField /> <ExpenseFormEntriesField />
</div> </div>
) );
} }

View File

@@ -1,7 +1,7 @@
import { FastField } from 'formik'; import { FastField } from 'formik';
import React from 'react'; import React from 'react';
import ExpenseFormEntriesTable from './ExpenseFormEntriesTable'; import ExpenseFormEntriesTable from './ExpenseFormEntriesTable';
import { useExpenseFormContext } from './ExpenseFormPageProvider'; import { defaultExpenseEntry } from './utils';
/** /**
* Expense form entries field. * Expense form entries field.
@@ -9,8 +9,6 @@ import { useExpenseFormContext } from './ExpenseFormPageProvider';
export default function ExpenseFormEntriesField({ export default function ExpenseFormEntriesField({
linesNumber = 4, linesNumber = 4,
}) { }) {
const { defaultCategoryEntry } = useExpenseFormContext();
return ( return (
<FastField name={'categories'}> <FastField name={'categories'}>
{({ form, field: { value }, meta: { error, touched } }) => ( {({ form, field: { value }, meta: { error, touched } }) => (
@@ -20,7 +18,7 @@ export default function ExpenseFormEntriesField({
onChange={(entries) => { onChange={(entries) => {
form.setFieldValue('categories', entries); form.setFieldValue('categories', entries);
}} }}
defaultEntry={defaultCategoryEntry} defaultEntry={defaultExpenseEntry}
linesNumber={linesNumber} linesNumber={linesNumber}
/> />
)} )}

View File

@@ -1,23 +1,21 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { Button } from '@blueprintjs/core';
import { FormattedMessage as T } from 'react-intl';
import { DataTableEditable } from 'components'; import { DataTableEditable } from 'components';
import ExpenseDeleteEntriesAlert from 'containers/Alerts/Expenses/ExpenseDeleteEntriesAlert';
import { useExpenseFormContext } from './ExpenseFormPageProvider'; import { useExpenseFormContext } from './ExpenseFormPageProvider';
import { useExpenseFormTableColumns } from './components'; import { useExpenseFormTableColumns } from './components';
import {
import withAlertActions from 'containers/Alert/withAlertActions'; saveInvoke,
compose,
import { transformUpdatedRows, compose, saveInvoke, repeatValue } from 'utils'; updateTableRow,
updateMinEntriesLines,
updateAutoAddNewLine,
updateRemoveLineByIndex,
} from 'utils';
/** /**
* Expenses form entries. * Expenses form entries.
*/ */
function ExpenseFormEntriesTable({ export default function ExpenseFormEntriesTable({
// #withAlertActions
openAlert,
// #ownPorps // #ownPorps
entries, entries,
defaultEntry, defaultEntry,
@@ -32,29 +30,30 @@ function ExpenseFormEntriesTable({
// Handles update datatable data. // Handles update datatable data.
const handleUpdateData = useCallback( const handleUpdateData = useCallback(
(rowIndex, columnIdOrObj, value) => { (rowIndex, columnId, value) => {
const newRows = transformUpdatedRows( const newRows = compose(
entries, updateAutoAddNewLine(defaultEntry, ['expense_account_id']),
rowIndex, updateTableRow(rowIndex, columnId, value),
columnIdOrObj, )(entries);
value,
);
saveInvoke(onChange, newRows); saveInvoke(onChange, newRows);
}, },
[entries, onChange], [entries, defaultEntry, onChange],
); );
// Handles click remove datatable row. // Handles click remove datatable row.
const handleRemoveRow = useCallback( const handleRemoveRow = useCallback(
(rowIndex) => { (rowIndex) => {
// Can't continue if there is just one row line or less. const newRows = compose(
if (entries.length <= 1) { // Ensure minimum lines count.
return; updateMinEntriesLines(4, defaultEntry),
} // Remove the line by the given index.
const newRows = entries.filter((row, index) => index !== rowIndex); updateRemoveLineByIndex(rowIndex),
)(entries);
saveInvoke(onChange, newRows); saveInvoke(onChange, newRows);
}, },
[entries, onChange], [entries, defaultEntry, onChange],
); );
return ( return (
@@ -72,6 +71,4 @@ function ExpenseFormEntriesTable({
footer={true} footer={true}
/> />
); );
} }
export default compose(withAlertActions)(ExpenseFormEntriesTable);

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react'; import React from 'react';
import { FastField } from 'formik'; import { FastField } from 'formik';
import classNames from 'classnames'; import classNames from 'classnames';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';

View File

@@ -93,17 +93,6 @@ export const compose = (...funcs) =>
); );
export const updateTableRow = (rowIndex, columnId, value) => (old) => {
return old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
}
}
return row
})
}
export const getObjectDiff = (a, b) => { export const getObjectDiff = (a, b) => {
return _.reduce( return _.reduce(
a, a,
@@ -600,4 +589,41 @@ export const amountPaymentEntries = (amount, entries) => {
payment_amount: diff, payment_amount: diff,
}; };
}); });
};
export const updateAutoAddNewLine = (defaultEntry, props) => (entries) => {
const newEntries = [...entries];
const lastEntry = _.last(newEntries);
const newLine = props.filter((entryKey) => !isBlank(lastEntry[entryKey]));
return newLine.length > 0 ? [...entries, defaultEntry] : [...entries];
};
/**
* Ensure min entries lines.
* @param {number} min
* @param {any} defaultEntry
*/
export const updateMinEntriesLines = (min, defaultEntry) => (lines) => {
if (lines.length < min) {
const diffLines = Math.max(min - lines.length, 0);
return [...lines, ...repeatValue(defaultEntry, diffLines)];
}
};
export const updateRemoveLineByIndex = (rowIndex) => (entries) => {
const removeIndex = parseInt(rowIndex, 10);
return entries.filter((row, index) => index !== removeIndex);
};
export const updateTableRow = (rowIndex, columnId, value) => (old) => {
return old.map((row, index) => {
if (index === rowIndex) {
return {
...old[rowIndex],
[columnId]: value,
}
}
return row
})
}; };

View File

@@ -172,26 +172,6 @@ export default class SalesReceiptService {
); );
} }
/**
* Retrieve estimate number to object model.
* @param {number} tenantId
* @param {ISaleReceiptDTO} saleReceiptDTO - Sale receipt DTO.
* @param {ISaleReceipt} oldSaleReceipt - Old receipt model object.
*/
transformReceiptNumberToModel(
tenantId: number,
saleReceiptDTO: ISaleReceiptDTO,
oldSaleReceipt?: ISaleReceipt
): string {
// Retreive the next invoice number.
const autoNextNumber = this.getNextReceiptNumber(tenantId);
if (saleReceiptDTO.receiptNumber) {
return saleReceiptDTO.receiptNumber;
}
return oldSaleReceipt ? oldSaleReceipt.receiptNumber : autoNextNumber;
}
/** /**
* Transform create DTO object to model object. * Transform create DTO object to model object.
* @param {ISaleReceiptDTO} saleReceiptDTO - * @param {ISaleReceiptDTO} saleReceiptDTO -
@@ -233,7 +213,7 @@ export default class SalesReceiptService {
receiptNumber, receiptNumber,
// Avoid rewrite the deliver date in edit mode when already published. // Avoid rewrite the deliver date in edit mode when already published.
...(saleReceiptDTO.closed && ...(saleReceiptDTO.closed &&
!oldSaleReceipt.closedAt && { !oldSaleReceipt?.closedAt && {
closedAt: moment().toMySqlDateTime(), closedAt: moment().toMySqlDateTime(),
}), }),
entries: saleReceiptDTO.entries.map((entry) => ({ entries: saleReceiptDTO.entries.map((entry) => ({