- fix: store children accounts with Redux store.

- fix: store expense payment date with transactions.
- fix: Total assets, liabilities and equity on balance sheet.
- tweaks: dashboard content and sidebar style.
- fix: reset form with contact list on journal entry form.
- feat: Add hints to filter accounts in financial statements.
This commit is contained in:
Ahmed Bouhuolia
2020-07-12 12:31:12 +02:00
parent 4bd8f1628d
commit 9d9c7c1568
60 changed files with 1685 additions and 929 deletions

View File

@@ -1,43 +1,51 @@
import React, { useCallback, useState } from 'react';
import {
MenuItem,
Button,
} from '@blueprintjs/core';
import React, { useCallback, useMemo } from 'react';
import { MenuItem, Button } from '@blueprintjs/core';
import ListSelect from 'components/ListSelect';
import { FormattedMessage as T } from 'react-intl';
export default function ContactsListField({
contacts,
onContactSelected,
error,
initialContact,
defautlSelectText = (<T id={'select_contact'} />)
selectedContactId,
selectedContactType,
defautlSelectText = <T id={'select_contact'} />,
}) {
const [selectedContact, setSelectedContact] = useState(
initialContact || null
// Contact item of select accounts field.
const contactRenderer = useCallback(
(item, { handleClick, modifiers, query }) => (
<MenuItem text={item.display_name} key={item.id} onClick={handleClick} />
),
[],
);
// Contact item of select accounts field.
const contactItem = useCallback((item, { handleClick, modifiers, query }) => (
<MenuItem text={item.display_name} key={item.id} onClick={handleClick} />
), []);
const onContactSelect = useCallback(
(contact) => {
onContactSelected && onContactSelected(contact);
},
[onContactSelected],
);
const onContactSelect = useCallback((contact) => {
setSelectedContact(contact.id);
onContactSelected && onContactSelected(contact);
}, [setSelectedContact, onContactSelected]);
const items = useMemo(
() =>
contacts.map((contact) => ({
...contact,
_id: `${contact.id}_${contact.contact_type}`,
})),
[contacts],
);
return (
<ListSelect
items={contacts}
noResults={<MenuItem disabled={true} text='No results.' />}
itemRenderer={contactItem}
items={items}
noResults={<MenuItem disabled={true} text="No results." />}
itemRenderer={contactRenderer}
popoverProps={{ minimal: true }}
filterable={true}
onItemSelect={onContactSelect}
labelProp={'display_name'}
selectedItem={selectedContact}
selectedItemProp={'id'}
defaultText={defautlSelectText} />
selectedItem={`${selectedContactId}_${selectedContactType}`}
selectedItemProp={'_id'}
defaultText={defautlSelectText}
/>
);
}
}

View File

@@ -110,10 +110,15 @@ function DashboardTopbar({
icon={<Icon icon={'plus-24'} iconSize={20} />}
text={<T id={'quick_new'} />}
/>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'notification-24'} iconSize={20} />}
/>
<Tooltip
content={<T id={'notifications'} />}
position={Position.BOTTOM}
>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'notification-24'} iconSize={20} />}
/>
</Tooltip>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'help-24'} iconSize={20} />}

View File

@@ -8,7 +8,13 @@ import {
useSortBy,
useFlexLayout,
} from 'react-table';
import { Checkbox, Spinner, ContextMenu, Menu, MenuItem } from '@blueprintjs/core';
import {
Checkbox,
Spinner,
ContextMenu,
Menu,
MenuItem,
} from '@blueprintjs/core';
import classnames from 'classnames';
import { FixedSizeList } from 'react-window';
import { useSticky } from 'react-table-sticky';
@@ -30,7 +36,7 @@ export default function DataTable({
loading,
onFetchData,
onSelectedRowsChange,
manualSortBy = 'false',
manualSortBy = false,
selectionColumn = false,
expandSubRows = true,
className,
@@ -51,7 +57,9 @@ export default function DataTable({
pagesCount: controlledPageCount,
initialPageIndex,
initialPageSize,
rowContextMenu
rowContextMenu,
expandColumnSpace = 1.5,
}) {
const {
getTableProps,
@@ -91,7 +99,6 @@ export default function DataTable({
manualSortBy,
expandSubRows,
payload,
autoResetSelectedRows: false,
},
useSortBy,
useExpanded,
@@ -148,7 +155,7 @@ export default function DataTable({
} else {
onFetchData && onFetchData({ pageIndex, pageSize, sortBy });
}
}, [pageIndex, pageSize, sortBy, onFetchData]);
}, [pageIndex, pageSize, manualSortBy ? sortBy : null, onFetchData]);
useUpdateEffect(() => {
onSelectedRowsChange && onSelectedRowsChange(selectedFlatRows);
@@ -162,7 +169,7 @@ export default function DataTable({
wrapper={(children) => (
<div
style={{
'padding-left': `${row.depth * 1.5}rem`,
'padding-left': `${row.depth * expandColumnSpace}rem`,
}}
>
{children}
@@ -198,9 +205,9 @@ export default function DataTable({
e.preventDefault();
const tr = e.currentTarget.closest('.tr');
tr.classList.add('is-context-menu-active');
const DropdownEl = rowContextMenu(cell, row);
ContextMenu.show(DropdownEl, { left: e.clientX, top: e.clientY }, () => {
tr.classList.remove('is-context-menu-active');
});
@@ -216,7 +223,9 @@ export default function DataTable({
return (
<div
{...row.getRowProps({
className: classnames('tr', rowClasses),
className: classnames('tr', {
'is-expanded': row.isExpanded && row.canExpand,
}, rowClasses),
style,
})}
>
@@ -369,7 +378,7 @@ export default function DataTable({
</ScrollSyncPane>
</div>
</ScrollSync>
<If condition={pagination && pageCount && !loading}>
<Pagination
initialPage={pageIndex + 1}

View File

@@ -1,12 +1,12 @@
import React, { useCallback, useMemo } from 'react';
import React, { useState, useCallback, useMemo } from 'react';
import { FormGroup, Intent, Classes } from "@blueprintjs/core";
import classNames from 'classnames';
import ContactsListField from 'components/ContactsListField';
export default function ContactsListCellRenderer({
column: { id, value },
column: { id },
row: { index, original },
cell: { value: initialValue },
cell: { value },
payload: { contacts, updateData, errors }
}) {
const handleContactSelected = useCallback((contact) => {
@@ -16,10 +16,6 @@ export default function ContactsListCellRenderer({
});
}, [updateData, index, id]);
const initialContact = useMemo(() => {
return contacts.find(c => c.id === initialValue);
}, [contacts, initialValue]);
const error = errors?.[index]?.[id];
return (
@@ -33,10 +29,10 @@ export default function ContactsListCellRenderer({
>
<ContactsListField
contacts={contacts}
onContactSelected={handleContactSelected}
initialContact={initialContact}
onContactSelected={handleContactSelected}
selectedContactId={original?.contact_id}
selectedContactType={original?.contact_type}
/>
</FormGroup>
)
);
}

View File

@@ -1,7 +1,7 @@
import React, { useMemo, useCallback } from 'react';
import moment from 'moment';
import classnames from 'classnames';
import LoadingIndicator from 'components/LoadingIndicator';
import { LoadingIndicator, MODIFIER } from 'components';
import { FormattedMessage as T, useIntl } from 'react-intl';
import { If } from 'components';
@@ -17,6 +17,8 @@ export default function FinancialSheet({
loading,
className,
basis,
minimal = false,
fullWidth = false
}) {
const { formatMessage } = useIntl();
const format = 'DD MMMM YYYY';
@@ -45,7 +47,12 @@ export default function FinancialSheet({
]);
return (
<div className={classnames('financial-sheet', nameModifer, className)}>
<div
className={classnames('financial-sheet', nameModifer, className, {
[MODIFIER.FINANCIAL_SHEET_MINIMAL]: minimal,
'is-full-width': fullWidth,
})}
>
<LoadingIndicator loading={loading} spinnerSize={34} />
<div
@@ -53,8 +60,14 @@ export default function FinancialSheet({
'is-loading': loading,
})}
>
<h1 class="financial-sheet__title">{companyName}</h1>
<h6 class="financial-sheet__sheet-type">{sheetType}</h6>
<If condition={!!companyName}>
<h1 class="financial-sheet__title">{companyName}</h1>
</If>
<If condition={!!sheetType}>
<h6 class="financial-sheet__sheet-type">{sheetType}</h6>
</If>
<div class="financial-sheet__date">
<If condition={asDate}>
<T id={'as'} /> {formattedAsDate}

View File

@@ -1,44 +1,68 @@
import React, { useState, useEffect } from 'react';
import {
Button,
MenuItem,
} from '@blueprintjs/core';
import React, { useState, useMemo, useEffect } from 'react';
import { Button, MenuItem } from '@blueprintjs/core';
import { Select } from '@blueprintjs/select';
import { FormattedMessage as T } from 'react-intl';
export default function ListSelect ({
export default function ListSelect({
buttonProps,
defaultText,
noResultsText = (<T id="no_results" />),
noResultsText = <T id="no_results" />,
isLoading = false,
labelProp,
selectedItem,
selectedItemProp = 'id',
// itemTextProp,
// itemLabelProp,
initialSelectedItem,
onItemSelect,
...selectProps
}) {
const [currentItem, setCurrentItem] = useState(null);
const selectedItemObj = useMemo(
() => selectProps.items.find((i) => i[selectedItemProp] === selectedItem),
[selectProps.items, selectedItemProp, selectedItem],
);
const selectedInitialItem = useMemo(
() => selectProps.items.find((i) => i[selectedItemProp] === initialSelectedItem),
[initialSelectedItem],
);
const [currentItem, setCurrentItem] = useState(
(initialSelectedItem && selectedInitialItem) || null,
);
useEffect(() => {
if (selectedItem && selectedItemProp) {
const item = selectProps.items.find(i => i[selectedItemProp] === selectedItem);
setCurrentItem(item);
if (selectedItemObj) {
setCurrentItem(selectedItemObj);
}
}, [selectedItem, selectedItemProp, selectProps.items]);
}, [selectedItemObj, setCurrentItem]);
const noResults = isLoading ?
('loading') : <MenuItem disabled={true} text={noResultsText} />;
const noResults = isLoading ? (
'loading'
) : (
<MenuItem disabled={true} text={noResultsText} />
);
const itemRenderer = (item, { handleClick, modifiers, query }) => {
return (<MenuItem text={item[labelProp]} key={item[selectedItemProp]} onClick={handleClick} />);
const itemRenderer = (item, { handleClick, modifiers, query }) => {
return (
<MenuItem
text={item[labelProp]}
key={item[selectedItemProp]}
onClick={handleClick}
/>
);
};
const handleItemSelect = (_item) => {
setCurrentItem(_item);
onItemSelect && onItemSelect(_item);
};
return (
<Select
itemRenderer={itemRenderer}
onItemSelect={handleItemSelect}
{...selectProps}
noResults={noResults}
>
@@ -48,5 +72,5 @@ export default function ListSelect ({
{...buttonProps}
/>
</Select>
)
}
);
}

View File

@@ -20,6 +20,7 @@ import AppToaster from './AppToaster';
import DataTable from './DataTable';
import AccountsSelectList from './AccountsSelectList';
import AccountsTypesSelect from './AccountsTypesSelect';
import LoadingIndicator from './LoadingIndicator';
const Hint = FieldHint;
@@ -47,4 +48,5 @@ export {
DataTable,
AccountsSelectList,
AccountsTypesSelect,
LoadingIndicator,
};

View File

@@ -1,4 +1,7 @@
export default {
SELECT_LIST_FILL_POPOVER: 'select-list--fill-popover',
SELECT_LIST_FILL_BUTTON: 'select-list--fill-button',
SELECT_LIST_TOOLTIP_ITEMS: 'select-list--tooltip-items',
FINANCIAL_SHEET_MINIMAL: 'financial-sheet--minimal'
}