- feat: Filter expense and payment accounts on expense form.

- feat: Make journal errors with receivable and payable accounts.
- fix: Handle database big numbers.
- fix: Indexing lines when add a new line on make journal form.
- fix: Abstruct accounts type component.
This commit is contained in:
Ahmed Bouhuolia
2020-07-06 21:22:27 +02:00
parent 3fc390652d
commit 282da55d08
40 changed files with 1031 additions and 747 deletions

View File

@@ -5,11 +5,10 @@ import { FormattedMessage as T } from 'react-intl';
export default function AccountsSelectList({
accounts,
onAccountSelected,
error = [],
initialAccountId,
selectedAccountId,
defaultSelectText = 'Select account',
onAccountSelected,
}) {
// Find initial account object to set it as default account in initial render.
const initialAccount = useMemo(

View File

@@ -0,0 +1,46 @@
import React, { useCallback } from 'react';
import {
ListSelect,
} from 'components';
export default function AccountsTypesSelect({
accountsTypes,
selectedTypeId,
defaultSelectText = 'Select account type',
onTypeSelected,
...restProps
}) {
// Filters accounts types items.
const filterAccountTypeItems = (query, accountType, _index, exactMatch) => {
const normalizedTitle = accountType.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
if (exactMatch) {
return normalizedTitle === normalizedQuery;
} else {
return normalizedTitle.indexOf(normalizedQuery) >= 0;
}
};
// Handle item selected.
const handleItemSelected = (accountType) => {
onTypeSelected && onTypeSelected(accountType);
};
const items = accountsTypes.map((type) => ({
id: type.id, name: type.name,
}));
return (
<ListSelect
items={items}
selectedItemProp={'id'}
selectedItem={selectedTypeId}
labelProp={'name'}
defaultText={defaultSelectText}
onItemSelect={handleItemSelected}
itemPredicate={filterAccountTypeItems}
{...restProps}
/>
);
}

View File

@@ -17,17 +17,9 @@ export default function ContactsListField({
initialContact || null
);
const contactTypeLabel = (contactType) => {
switch(contactType) {
case 'customer':
return 'Customer';
case 'vendor':
return 'Vendor';
}
};
// Contact item of select accounts field.
const contactItem = useCallback((item, { handleClick, modifiers, query }) => (
<MenuItem text={item.display_name} label={contactTypeLabel(item.contact_type)} key={item.id} onClick={handleClick} />
<MenuItem text={item.display_name} key={item.id} onClick={handleClick} />
), []);
const onContactSelect = useCallback((contact) => {

View File

@@ -1,44 +1,43 @@
import React, {useCallback, useMemo} from 'react';
import React, { useCallback, useMemo } from 'react';
import AccountsSelectList from 'components/AccountsSelectList';
import classNames from 'classnames';
import {
FormGroup,
Classes,
Intent,
} from '@blueprintjs/core';
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
// Account cell renderer.
const AccountCellRenderer = ({
column: { id },
column: { id, accountsDataProp },
row: { index, original },
cell: { value: initialValue },
payload: { accounts, updateData, errors },
payload: { accounts: defaultAccounts, updateData, errors, ...restProps },
}) => {
const handleAccountSelected = useCallback((account) => {
updateData(index, id, account.id);
}, [updateData, index, id]);
const { account_id = false } = (errors[index] || {});
// const initialAccount = useMemo(() =>
// accounts.find(a => a.id === initialValue),
// [accounts, initialValue]);
const handleAccountSelected = useCallback(
(account) => {
updateData(index, id, account.id);
},
[updateData, index, id],
);
const error = errors?.[index]?.[id];
const accounts = useMemo(
() => restProps[accountsDataProp] || defaultAccounts,
[restProps, defaultAccounts, accountsDataProp],
);
return (
<FormGroup
intent={account_id ? Intent.DANGER : ''}
intent={error ? Intent.DANGER : null}
className={classNames(
'form-group--select-list',
'form-group--account',
Classes.FILL)}
>
Classes.FILL,
)}
>
<AccountsSelectList
accounts={accounts}
onAccountSelected={handleAccountSelected}
error={account_id}
selectedAccountId={initialValue} />
selectedAccountId={initialValue}
/>
</FormGroup>
);
};
export default AccountCellRenderer;
export default AccountCellRenderer;

View File

@@ -1,5 +1,5 @@
import React, { useCallback, useMemo } from 'react';
import { FormGroup, Classes } from "@blueprintjs/core";
import { FormGroup, Intent, Classes } from "@blueprintjs/core";
import classNames from 'classnames';
import ContactsListField from 'components/ContactsListField';
@@ -20,8 +20,11 @@ export default function ContactsListCellRenderer({
return contacts.find(c => c.id === initialValue);
}, [contacts, initialValue]);
const error = errors?.[index]?.[id];
return (
<FormGroup
intent={error ? Intent.DANGER : null}
className={classNames(
'form-group--select-list',
'form-group--contacts-list',
@@ -32,6 +35,7 @@ export default function ContactsListCellRenderer({
contacts={contacts}
onContactSelected={handleContactSelected}
initialContact={initialContact}
/>
</FormGroup>
)

View File

@@ -1,31 +1,35 @@
import React, {useState, useEffect} from 'react';
import {
InputGroup
} from '@blueprintjs/core';
import React, { useState, useEffect } from 'react';
import classNames from 'classnames';
import { Classes, InputGroup, FormGroup } from '@blueprintjs/core';
const InputEditableCell = ({
row: { index },
column: { id, },
column: { id },
cell: { value: initialValue },
payload,
}) => {
const [value, setValue] = useState(initialValue)
const [value, setValue] = useState(initialValue);
const onChange = e => {
setValue(e.target.value)
}
const onChange = (e) => {
setValue(e.target.value);
};
const onBlur = () => {
payload.updateData(index, id, value)
}
payload.updateData(index, id, value);
};
useEffect(() => {
setValue(initialValue)
}, [initialValue])
setValue(initialValue);
}, [initialValue]);
return (<InputGroup
value={value}
onChange={onChange}
onBlur={onBlur}
fill={true} />);
return (
<FormGroup>
<InputGroup
value={value}
onChange={onChange}
onBlur={onBlur}
fill={true}
/>
</FormGroup>
);
};
export default InputEditableCell;

View File

@@ -1,4 +1,5 @@
import React, { useCallback, useState, useEffect } from 'react';
import { FormGroup, Intent } from '@blueprintjs/core';
import MoneyInputGroup from 'components/MoneyInputGroup';
// Input form cell renderer.
@@ -6,7 +7,7 @@ const MoneyFieldCellRenderer = ({
row: { index },
column: { id },
cell: { value: initialValue },
payload
payload: { errors, updateData },
}) => {
const [value, setValue] = useState(initialValue);
@@ -15,26 +16,36 @@ const MoneyFieldCellRenderer = ({
}, []);
function isNumeric(data) {
return !isNaN(parseFloat(data)) && isFinite(data) && data.constructor !== Array;
return (
!isNaN(parseFloat(data)) && isFinite(data) && data.constructor !== Array
);
}
const onBlur = () => {
const updateValue = isNumeric(value) ? parseFloat(value) : value;
payload.updateData(index, id, updateValue);
updateData(index, id, updateValue);
};
useEffect(() => {
setValue(initialValue);
}, [initialValue])
}, [initialValue]);
return (<MoneyInputGroup
value={value}
prefix={'$'}
onChange={handleFieldChange}
inputGroupProps={{
fill: true,
onBlur,
}} />)
const error = errors?.[index]?.[id];
return (
<FormGroup
intent={error ? Intent.DANGER : null}>
<MoneyInputGroup
value={value}
prefix={'$'}
onChange={handleFieldChange}
inputGroupProps={{
fill: true,
onBlur,
}}
/>
</FormGroup>
);
};
export default MoneyFieldCellRenderer;
export default MoneyFieldCellRenderer;

View File

@@ -33,7 +33,7 @@ export default function ListSelect ({
('loading') : <MenuItem disabled={true} text={noResultsText} />;
const itemRenderer = (item, { handleClick, modifiers, query }) => {
return (<MenuItem text={item[labelProp]} key={item[selectedItemProp]} />);
return (<MenuItem text={item[labelProp]} key={item[selectedItemProp]} onClick={handleClick} />);
};
return (

View File

@@ -19,6 +19,7 @@ import Dialog from './Dialog';
import AppToaster from './AppToaster';
import DataTable from './DataTable';
import AccountsSelectList from './AccountsSelectList';
import AccountsTypesSelect from './AccountsTypesSelect';
const Hint = FieldHint;
@@ -45,4 +46,5 @@ export {
AppToaster,
DataTable,
AccountsSelectList,
AccountsTypesSelect,
};