WIP Make journal entries.

This commit is contained in:
Ahmed Bouhuolia
2020-04-07 12:56:12 +02:00
parent 5f6c7ca2d5
commit 490979ded5
19 changed files with 632 additions and 197 deletions

View File

@@ -0,0 +1,47 @@
import React, {useMemo, useCallback, useState} from 'react';
import {omit} from 'lodash';
import {
MenuItem,
Button
} from '@blueprintjs/core';
import {Select} from '@blueprintjs/select';
// import MultiSelect from 'components/MultiSelect';
export default function AccountsMultiSelect({
accounts,
onAccountSelected,
}) {
const [selectedAccount, setSelectedAccount] = useState(null);
// Account item of select accounts field.
const accountItem = useCallback((item, { handleClick, modifiers, query }) => {
return (
<MenuItem
text={item.name}
label={item.code}
key={item.id}
onClick={handleClick} />
);
}, []);
const onAccountSelect = useCallback((account) => {
setSelectedAccount({ ...account });
onAccountSelected && onAccountSelected(account);
}, [setSelectedAccount, onAccountSelected]);
return (
<Select
items={accounts}
noResults={<MenuItem disabled={true} text='No results.' />}
itemRenderer={accountItem}
popoverProps={{ minimal: true }}
filterable={true}
onItemSelect={onAccountSelect}
>
<Button
rightIcon='caret-down'
text={selectedAccount ? selectedAccount.name : 'Select account'}
/>
</Select>
);
}

View File

@@ -0,0 +1,62 @@
import React, {useCallback} from 'react';
import {
FormGroup,
MenuItem,
Button,
} from '@blueprintjs/core';
import {
Select
} from '@blueprintjs/select';
export default function CurrenciesSelectList(props) {
const {formGroupProps, selectProps, onItemSelect} = props;
const currencies = [{
name: 'USD US dollars', key: 'USD',
name: 'CAD Canadian dollars', key: 'CAD',
}];
// Handle currency item select.
const onCurrencySelect = useCallback((currency) => {
onItemSelect && onItemSelect(currency);
}, [onItemSelect]);
// Filters currencies list.
const filterCurrenciesPredicator = useCallback((query, currency, _index, exactMatch) => {
const normalizedTitle = currency.name.toLowerCase();
const normalizedQuery = query.toLowerCase();
return `${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
}, []);
// Currency item of select currencies field.
const currencyItem = (item, { handleClick, modifiers, query }) => {
return (
<MenuItem text={item.name} label={item.code} key={item.id} onClick={handleClick} />
);
};
return (
<FormGroup
label={'Currency'}
className={'form-group--select-list form-group--currency'}
{...formGroupProps}
>
<Select
items={currencies}
noResults={<MenuItem disabled={true} text='No results.' />}
itemRenderer={currencyItem}
itemPredicate={filterCurrenciesPredicator}
popoverProps={{ minimal: true }}
onItemSelect={onCurrencySelect}
{...selectProps}
>
<Button
rightIcon='caret-down'
text={'USD US dollars'}
/>
</Select>
</FormGroup>
);
}

View File

@@ -44,6 +44,7 @@ export default function DataTable({
virtualizedRows = false,
fixedSizeHeight = 100,
fixedItemSize = 30,
payload,
}) {
const {
getTableProps,
@@ -78,6 +79,7 @@ export default function DataTable({
getSubRows: row => row.children,
manualSortBy,
expandSubRows,
payload,
},
useSortBy,
useExpanded,
@@ -122,7 +124,7 @@ export default function DataTable({
// Renders table row.
const RenderRow = useCallback(({ style = {}, row }) => {
prepareRow(row);
const rowClasses = rowClassNames && rowClassNames(row.original);
const rowClasses = rowClassNames && rowClassNames(row);
return (
<div {...row.getRowProps({

View File

@@ -0,0 +1,26 @@
import React from 'react';
import AccountsSelectList from 'components/AccountsSelectList';
import classNames from 'classnames';
import {
FormGroup,
Classes,
} from '@blueprintjs/core';
// Account cell renderer.
const AccountCellRenderer = ({
row: { index, original },
payload: { accounts }
}) => {
return (
<FormGroup
className={classNames('form-group--select-list',
'form-group--account', Classes.FILL)}
>
<AccountsSelectList
accounts={accounts}
onAccountSelected={() => {}} />
</FormGroup>
);
};
export default AccountCellRenderer;

View File

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

View File

@@ -0,0 +1,31 @@
import React, { useCallback, useState } from 'react';
import MoneyInputGroup from 'components/MoneyInputGroup';
// Input form cell renderer.
const MoneyFieldCellRenderer = ({
row: { index },
column: { id },
cell: { value: initialValue },
payload
}) => {
const [value, setValue] = useState(initialValue);
const handleFieldChange = useCallback((e, value) => {
setValue(value);
}, []);
const onBlur = () => {
payload.updateData(index, id, value)
};
return (<MoneyInputGroup
value={value}
prefix={'$'}
onChange={handleFieldChange}
inputGroupProps={{
fill: true,
onBlur,
}} />)
};
export default MoneyFieldCellRenderer;

View File

@@ -0,0 +1,9 @@
import AccountsListFieldCell from './AccountsListFieldCell';
import MoneyFieldCell from './MoneyFieldCell';
import InputGroupCell from './InputGroupCell';
export {
AccountsListFieldCell,
MoneyFieldCell,
InputGroupCell,
}

View File

@@ -1,13 +1,5 @@
import React from 'react';
import Currency from 'js-money/lib/currency';
import accounting from 'accounting';
function formattedAmount(cents, currency) {
const { symbol, decimal_digits: precision } = Currency[currency];
const amount = cents / Math.pow(10, precision);
return accounting.formatMoney(amount, { symbol, precision });
}
import {formattedAmount} from 'utils';
export default function Money({ amount, currency }) {
return (

View File

@@ -0,0 +1,88 @@
import React, {useState, useMemo, useEffect, useCallback} from 'react';
import {InputGroup} from '@blueprintjs/core';
const joinIntegerAndDecimal = (integer, decimal, separator) => {
let output = `${integer}`;
if (separator) {
output += separator;
output += decimal ? decimal : '';
}
return output;
};
const hasSeparator = (input, separator) => {
return -1 !== input.indexOf(separator);
};
const addThousandSeparator = (integer, separator) => {
return integer.replace(/(\d)(?=(?:\d{3})+\b)/gm, `$1${separator}`)
};
const toString = (number) => `${number}`;
const onlyNumbers = (input) => {
return toString(input).replace(/\D+/g, '') || '0'
};
const formatter = (value, options) => {
const input = toString(value);
const navigate = input.indexOf('-') >= 0 ? '-' : '';
const parts = toString(input).split(options.decimal);
const integer = parseInt(onlyNumbers(parts[0]), 10);
const decimal = parts[1] ? onlyNumbers(parts[1]) : null;
const integerThousand = addThousandSeparator(toString(integer), options.thousands);
const separator = hasSeparator(input, options.decimal)
? options.decimal : false;
return `${navigate}${options.prefix}${joinIntegerAndDecimal(integerThousand, decimal, separator)}${options.suffix}`;
};
const unformatter = (input, options) => {
const navigate = input.indexOf('-') >= 0 ? '-' : '';
const parts = toString(input).split(options.decimal);
const integer = parseInt(onlyNumbers(parts[0]), 10);
const decimal = parts[1] ? onlyNumbers(parts[1]) : null;
const separator = hasSeparator(input, options.decimal)
? options.decimal : false;
return `${navigate}${joinIntegerAndDecimal(integer, decimal, separator)}`;
};
export default function MoneyFieldGroup({
value,
prefix = '',
suffix = '',
thousands = ',',
decimal = '.',
precision = 2,
inputGroupProps,
onChange,
}) {
const [state, setState] = useState(value);
const options = useMemo(() => ({
prefix, suffix, thousands, decimal, precision,
}), []);
const handleChange = useCallback((event) => {
const formatted = formatter(event.target.value, options);
const value = unformatter(event.target.value, options);
setState(formatted);
onChange && onChange(event, value);
}, []);
useEffect(() => {
const formatted = formatter(value, options);
setState(formatted)
}, []);
return (
<InputGroup
value={state}
onChange={handleChange}
{...inputGroupProps} />
);
}