feat: make journal auto-adjustment for entries.

feat: auto-focus cells inside the table.
This commit is contained in:
a.bouhuolia
2021-01-27 15:02:12 +02:00
parent 81c59f8a1f
commit f8ee455985
9 changed files with 121 additions and 27 deletions

View File

@@ -4,7 +4,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@babel/core": "7.8.4", "@babel/core": "7.8.4",
"@blueprintjs/core": "^3.23.1", "@blueprintjs/core": "^3.38.1",
"@blueprintjs/datetime": "^3.15.2", "@blueprintjs/datetime": "^3.15.2",
"@blueprintjs/select": "^3.11.2", "@blueprintjs/select": "^3.11.2",
"@blueprintjs/table": "^3.8.3", "@blueprintjs/table": "^3.8.3",

View File

@@ -1,11 +1,14 @@
import React, { useCallback, useMemo } from 'react'; import React, { useRef, useCallback, useMemo } from 'react';
import AccountsSuggestField from 'components/AccountsSuggestField';
// import AccountsSelectList from 'components/AccountsSelectList';
import classNames from 'classnames'; import classNames from 'classnames';
import { useCellAutoFocus } from 'hooks';
import AccountsSuggestField from 'components/AccountsSuggestField';
// import AccountsSelectList from 'components/AccountsSelectList';
import { FormGroup, Classes, Intent } from '@blueprintjs/core'; import { FormGroup, Classes, Intent } from '@blueprintjs/core';
// Account cell renderer. // Account cell renderer.
const AccountCellRenderer = ({ export default function AccountCellRenderer({
column: { column: {
id, id,
accountsDataProp, accountsDataProp,
@@ -18,9 +21,14 @@ const AccountCellRenderer = ({
accounts: defaultAccounts, accounts: defaultAccounts,
updateData, updateData,
errors, errors,
autoFocus,
...restPayloadProps ...restPayloadProps
}, },
}) => { }) {
const accountRef = useRef();
useCellAutoFocus(accountRef, autoFocus, id, index);
const handleAccountSelected = useCallback( const handleAccountSelected = useCallback(
(account) => { (account) => {
updateData(index, id, account.id); updateData(index, id, account.id);
@@ -49,9 +57,12 @@ const AccountCellRenderer = ({
selectedAccountId={initialValue} selectedAccountId={initialValue}
filterByRootTypes={filterAccountsByRootType} filterByRootTypes={filterAccountsByRootType}
filterByTypes={filterAccountsByTypes} filterByTypes={filterAccountsByTypes}
inputProps={{
inputRef: (ref) => (accountRef.current = ref),
}}
openOnKeyDown={true}
blurOnSelectClose={false}
/> />
</FormGroup> </FormGroup>
); );
}; }
export default AccountCellRenderer;

View File

@@ -1,15 +1,23 @@
import React, { useCallback } from 'react'; import React, { useCallback, useRef } from 'react';
// import ItemsListField from 'components/ItemsListField'; // import ItemsListField from 'components/ItemsListField';
import ItemsSuggestField from 'components/ItemsSuggestField'; import ItemsSuggestField from 'components/ItemsSuggestField';
import classNames from 'classnames'; import classNames from 'classnames';
import { FormGroup, Classes, Intent } from '@blueprintjs/core'; import { FormGroup, Classes, Intent } from '@blueprintjs/core';
import { useCellAutoFocus } from 'hooks';
export default function ItemsListCell({ export default function ItemsListCell({
column: { id, filterSellable, filterPurchasable }, column: { id, filterSellable, filterPurchasable },
row: { index }, row: { index },
cell: { value: initialValue }, cell: { value: initialValue },
payload: { items, updateData, errors }, payload: { items, updateData, errors, autoFocus },
}) { }) {
const fieldRef = useRef();
// Auto-focus the items list input field.
useCellAutoFocus(fieldRef, autoFocus, id, index);
const handleItemSelected = useCallback( const handleItemSelected = useCallback(
(item) => { (item) => {
updateData(index, id, item.id); updateData(index, id, item.id);
@@ -30,6 +38,11 @@ export default function ItemsListCell({
selectedItemId={initialValue} selectedItemId={initialValue}
sellable={filterSellable} sellable={filterSellable}
purchasable={filterPurchasable} purchasable={filterPurchasable}
inputProps={{
inputRef: (ref) => (fieldRef.current = ref),
}}
openOnKeyDown={true}
blurOnSelectClose={false}
/> />
</FormGroup> </FormGroup>
); );

View File

@@ -2,11 +2,7 @@ import React, { useState, useMemo, useEffect, useCallback } from 'react';
import { Button } from '@blueprintjs/core'; import { Button } from '@blueprintjs/core';
import { FormattedMessage as T, useIntl } from 'react-intl'; import { FormattedMessage as T, useIntl } from 'react-intl';
import { omit } from 'lodash'; import { omit } from 'lodash';
import classNames from 'classnames'; import { compose, saveInvoke } from 'utils';
import { CLASSES } from 'common/classes';
import DataTable from 'components/DataTable';
import { compose, transformUpdatedRows, saveInvoke } from 'utils';
import { import {
AccountsListFieldCell, AccountsListFieldCell,
MoneyFieldCell, MoneyFieldCell,
@@ -21,9 +17,12 @@ import {
NoteCellRenderer, NoteCellRenderer,
} from './components'; } from './components';
import { DataTableEditable } from 'components'; import { DataTableEditable } from 'components';
import withAccounts from 'containers/Accounts/withAccounts'; import withAccounts from 'containers/Accounts/withAccounts';
import withCustomers from 'containers/Customers/withCustomers'; import withCustomers from 'containers/Customers/withCustomers';
import { updateDataReducer } from './utils';
/** /**
* Make journal entries table component. * Make journal entries table component.
*/ */
@@ -126,8 +125,9 @@ function MakeJournalEntriesTable({
}; };
// Handles update datatable data. // Handles update datatable data.
const handleUpdateData = (rowIndex, columnIdOrObj, value) => { const handleUpdateData = (rowIndex, columnId, value) => {
const newRows = transformUpdatedRows(rows, rowIndex, columnIdOrObj, value); const newRows = updateDataReducer(rows, rowIndex, columnId, value);
saveInvoke( saveInvoke(
onChange, onChange,
newRows newRows
@@ -185,6 +185,7 @@ function MakeJournalEntriesTable({
contact_type: 'customer', contact_type: 'customer',
})), })),
], ],
autoFocus: ['account_id', 0],
}} }}
actions={ actions={
<> <>

View File

@@ -91,7 +91,6 @@ export default function MakeJournalFloatingAction({
<Button <Button
disabled={isSubmitting} disabled={isSubmitting}
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
type="submit"
onClick={handleSubmitPublishBtnClick} onClick={handleSubmitPublishBtnClick}
text={<T id={'save_publish'} />} text={<T id={'save_publish'} />}
/> />
@@ -123,7 +122,6 @@ export default function MakeJournalFloatingAction({
<Button <Button
disabled={isSubmitting} disabled={isSubmitting}
className={'ml1'} className={'ml1'}
type="submit"
onClick={handleSubmitDraftBtnClick} onClick={handleSubmitDraftBtnClick}
text={<T id={'save_as_draft'} />} text={<T id={'save_as_draft'} />}
/> />
@@ -156,7 +154,6 @@ export default function MakeJournalFloatingAction({
<Button <Button
disabled={isSubmitting} disabled={isSubmitting}
intent={Intent.PRIMARY} intent={Intent.PRIMARY}
type="submit"
onClick={handleSubmitPublishBtnClick} onClick={handleSubmitPublishBtnClick}
text={<T id={'save'} />} text={<T id={'save'} />}
/> />

View File

@@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import { get, sumBy, setWith, toSafeInteger } from 'lodash';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { formatMessage } from 'services/intl'; import { formatMessage } from 'services/intl';
import { setWith } from 'lodash'; import { transformUpdatedRows } from 'utils';
const ERROR = { const ERROR = {
JOURNAL_NUMBER_ALREADY_EXISTS: 'JOURNAL.NUMBER.ALREADY.EXISTS', JOURNAL_NUMBER_ALREADY_EXISTS: 'JOURNAL.NUMBER.ALREADY.EXISTS',
@@ -15,6 +16,46 @@ const ERROR = {
ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT', ENTRIES_SHOULD_ASSIGN_WITH_CONTACT: 'ENTRIES_SHOULD_ASSIGN_WITH_CONTACT',
}; };
function adjustmentEntries(entries) {
const credit = sumBy(entries, e => toSafeInteger(e.credit));
const debit = sumBy(entries, e => toSafeInteger(e.debit));
return {
debit: Math.max(credit - debit, 0),
credit: Math.max(debit - credit, 0),
}
}
export const updateDataReducer = (rows, rowIndex, columnId, value) => {
let newRows = transformUpdatedRows(rows, rowIndex, columnId, value);
const oldCredit = get(rows, `[${rowIndex}].credit`);
const oldDebit = get(rows, `[${rowIndex}].debit`);
if (columnId === 'account_id' && !oldCredit && !oldDebit) {
const adjustment = adjustmentEntries(rows);
if (adjustment.credit) {
newRows = transformUpdatedRows(
newRows,
rowIndex,
'credit',
adjustment.credit,
);
}
if (adjustment.debit) {
newRows = transformUpdatedRows(
newRows,
rowIndex,
'debit',
adjustment.debit,
);
}
}
return newRows;
};
// Transform API errors in toasts messages. // Transform API errors in toasts messages.
export const transformErrors = (resErrors, { setErrors, errors }) => { export const transformErrors = (resErrors, { setErrors, errors }) => {
const getError = (errorType) => resErrors.find((e) => e.type === errorType); const getError = (errorType) => resErrors.find((e) => e.type === errorType);

View File

@@ -233,6 +233,7 @@ function ItemsEntriesTable({
errors: errors || [], errors: errors || [],
updateData: handleUpdateData, updateData: handleUpdateData,
removeRow: handleRemoveRow, removeRow: handleRemoveRow,
autoFocus: ['item_id', 0],
}} }}
actions={ actions={
<> <>

View File

@@ -1,4 +1,4 @@
import {useRef, useEffect} from 'react'; import {useRef, useEffect, useMemo } from 'react';
import useAsync from './async'; import useAsync from './async';
import useAutofocus from './useAutofocus'; import useAutofocus from './useAutofocus';
@@ -35,7 +35,37 @@ export function useIsValuePassed(value, compatatorValue) {
return cache.current.indexOf(compatatorValue) !== -1; return cache.current.indexOf(compatatorValue) !== -1;
} }
const isCurrentFocus = (autoFocus, columnId, rowIndex) => {
let _columnId;
let _rowIndex;
if (Array.isArray(autoFocus)) {
_columnId = autoFocus[0];
_rowIndex = autoFocus[1] || 0;
}
_rowIndex = parseInt(_rowIndex, 10);
return columnId === _columnId && _rowIndex === rowIndex;
};
export function useCellAutoFocus(ref, autoFocus, columnId, rowIndex) {
const focus = useMemo(() => isCurrentFocus(autoFocus, columnId, rowIndex), [
autoFocus,
columnId,
rowIndex,
]);
useEffect(() => {
if (ref.current && focus) {
ref.current.focus();
}
}, [ref, focus]);
return ref;
}
export { export {
useAsync, useAsync,
useAutofocus, useAutofocus,
} }

View File

@@ -1,13 +1,13 @@
import { useRef, useEffect } from 'react'; import { useRef, useEffect } from 'react';
export default function useAutofocus() { export default function useAutofocus(focus = true) {
const ref = useRef(); const ref = useRef();
useEffect(() => { useEffect(() => {
if (ref.current) { if (ref.current && focus) {
ref.current.focus(); ref.current.focus();
} }
}, [ref]); }, [ref, focus]);
return ref; return ref;
} }