mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
WIP Make journal entries.
This commit is contained in:
47
client/src/components/AccountsSelectList.js
Normal file
47
client/src/components/AccountsSelectList.js
Normal 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>
|
||||
);
|
||||
}
|
||||
62
client/src/components/CurrenciesSelectList.js
Normal file
62
client/src/components/CurrenciesSelectList.js
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
31
client/src/components/DataTableCells/InputGroupCell.js
Normal file
31
client/src/components/DataTableCells/InputGroupCell.js
Normal 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;
|
||||
31
client/src/components/DataTableCells/MoneyFieldCell.js
Normal file
31
client/src/components/DataTableCells/MoneyFieldCell.js
Normal 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;
|
||||
9
client/src/components/DataTableCells/index.js
Normal file
9
client/src/components/DataTableCells/index.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import AccountsListFieldCell from './AccountsListFieldCell';
|
||||
import MoneyFieldCell from './MoneyFieldCell';
|
||||
import InputGroupCell from './InputGroupCell';
|
||||
|
||||
export {
|
||||
AccountsListFieldCell,
|
||||
MoneyFieldCell,
|
||||
InputGroupCell,
|
||||
}
|
||||
@@ -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 (
|
||||
|
||||
88
client/src/components/MoneyInputGroup.js
Normal file
88
client/src/components/MoneyInputGroup.js
Normal 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} />
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user