mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-15 20:30:33 +00:00
- 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:
@@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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} />}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
@@ -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'
|
||||
}
|
||||
Reference in New Issue
Block a user