mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
- feat: Update react-query package to V 2.1.1.
- feat: Favicon setup. - feat: Fix accounts inactivate/activate 1 account. - feat: Seed accounts, expenses and manual journals resource fields. - feat: Validate make journal receivable/payable without contact. - feat: Validate make journal contact without receivable or payable. - feat: More components abstractions. - feat: Use reselect.js to memorize components properties. - fix: Journal type of manual journal. - fix: Sidebar style optimization. - fix: Data-table check-box style optimization. - fix: Data-table spinner style dimensions. - fix: Submit journal with contact_id and contact_type.
This commit is contained in:
@@ -27,11 +27,29 @@ export default function AccountsSelectList({
|
||||
onAccountSelected && onAccountSelected(account);
|
||||
}, [setSelectedAccount, onAccountSelected]);
|
||||
|
||||
// Filters accounts items.
|
||||
const filterAccountsPredicater = useCallback(
|
||||
(query, account, _index, exactMatch) => {
|
||||
const normalizedTitle = account.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return (
|
||||
`${account.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0
|
||||
);
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
items={accounts}
|
||||
noResults={<MenuItem disabled={true} text='No results.' />}
|
||||
itemRenderer={accountItem}
|
||||
itemPredicate={filterAccountsPredicater}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={true}
|
||||
onItemSelect={onAccountSelect}>
|
||||
|
||||
@@ -17,9 +17,9 @@ function App({ locale }) {
|
||||
const history = createBrowserHistory();
|
||||
|
||||
const queryConfig = {
|
||||
refetchAllOnWindowFocus: false,
|
||||
cacheTime: 10000,
|
||||
staleTime: 10000,
|
||||
queries: {
|
||||
refetchOnWindowFocus: true,
|
||||
}
|
||||
};
|
||||
return (
|
||||
<IntlProvider locale={locale} messages={messages}>
|
||||
|
||||
@@ -32,7 +32,7 @@ export default function ContactsListField({
|
||||
|
||||
const onContactSelect = useCallback((contact) => {
|
||||
setSelectedContact(contact.id);
|
||||
onContactSelected && onContactSelected(contact.id);
|
||||
onContactSelected && onContactSelected(contact);
|
||||
}, [setSelectedContact, onContactSelected]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -9,11 +9,15 @@ import {
|
||||
import {
|
||||
Select
|
||||
} from '@blueprintjs/select';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
export default function CurrenciesSelectList(props) {
|
||||
const {formGroupProps, selectProps, onItemSelect} = props;
|
||||
export default function CurrenciesSelectList({
|
||||
formGroupProps,
|
||||
selectProps,
|
||||
onItemSelect,
|
||||
className,
|
||||
}) {
|
||||
const currencies = [{
|
||||
name: 'USD US dollars', key: 'USD',
|
||||
name: 'CAD Canadian dollars', key: 'CAD',
|
||||
@@ -41,7 +45,13 @@ export default function CurrenciesSelectList(props) {
|
||||
return (
|
||||
<FormGroup
|
||||
label={<T id={'currency'}/>}
|
||||
className={'form-group--select-list form-group--currency'}
|
||||
className={
|
||||
classNames(
|
||||
'form-group--select-list',
|
||||
'form-group--currency',
|
||||
className,
|
||||
)
|
||||
}
|
||||
{...formGroupProps}
|
||||
>
|
||||
<Select
|
||||
@@ -54,7 +64,6 @@ export default function CurrenciesSelectList(props) {
|
||||
{...selectProps}
|
||||
>
|
||||
<Button
|
||||
rightIcon='caret-down'
|
||||
text={'USD US dollars'}
|
||||
/>
|
||||
</Select>
|
||||
|
||||
@@ -14,17 +14,15 @@ import { FormattedMessage as T } from 'react-intl';
|
||||
|
||||
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
|
||||
import DashboardBreadcrumbs from 'components/Dashboard/DashboardBreadcrumbs';
|
||||
import Icon from 'components/Icon';
|
||||
import { Icon, If } from 'components';
|
||||
|
||||
import withSearch from 'containers/GeneralSearch/withSearch'
|
||||
import withSearch from 'containers/GeneralSearch/withSearch';
|
||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
function DashboardTopbar({
|
||||
|
||||
// #withDashboard
|
||||
pageTitle,
|
||||
pageSubtitle,
|
||||
@@ -42,69 +40,75 @@ function DashboardTopbar({
|
||||
history.push(`/custom_views/${editViewId}/edit`);
|
||||
};
|
||||
|
||||
const maybleRenderPageSubtitle = pageSubtitle && <h3>{pageSubtitle}</h3>;
|
||||
const maybeRenderEditViewBtn = pageSubtitle && editViewId && (
|
||||
<Button
|
||||
className={Classes.MINIMAL + ' button--view-edit'}
|
||||
icon={<Icon icon='pen' iconSize={13} />}
|
||||
onClick={handlerClickEditView}
|
||||
/>
|
||||
);
|
||||
|
||||
const handleSidebarToggleBtn = () => {
|
||||
toggleSidebarExpend();
|
||||
};
|
||||
return (
|
||||
<div class='dashboard__topbar'>
|
||||
<div class='dashboard__topbar-left'>
|
||||
<div class='dashboard__topbar-sidebar-toggle'>
|
||||
<Tooltip content={<T id={'close_sidebar'} />} position={Position.RIGHT}>
|
||||
<div class="dashboard__topbar">
|
||||
<div class="dashboard__topbar-left">
|
||||
<div class="dashboard__topbar-sidebar-toggle">
|
||||
<Tooltip
|
||||
content={<T id={'close_sidebar'} />}
|
||||
position={Position.RIGHT}
|
||||
>
|
||||
<Button minimal={true} onClick={handleSidebarToggleBtn}>
|
||||
<svg
|
||||
xmlns='http://www.w3.org/2000/svg'
|
||||
width='20'
|
||||
height='20'
|
||||
viewBox='0 0 20 20'
|
||||
role='img'
|
||||
focusable='false'
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
role="img"
|
||||
focusable="false"
|
||||
>
|
||||
<title><T id={'menu'}/></title>
|
||||
<title>
|
||||
<T id={'menu'} />
|
||||
</title>
|
||||
<path
|
||||
stroke='currentColor'
|
||||
stroke-linecap='round'
|
||||
stroke-miterlimit='5'
|
||||
stroke-width='2'
|
||||
d='M4 7h15M4 12h15M4 17h15'
|
||||
stroke="currentColor"
|
||||
stroke-linecap="round"
|
||||
stroke-miterlimit="5"
|
||||
stroke-width="2"
|
||||
d="M4 7h15M4 12h15M4 17h15"
|
||||
></path>
|
||||
</svg>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
<div class='dashboard__title'>
|
||||
<div class="dashboard__title">
|
||||
<h1>{pageTitle}</h1>
|
||||
{maybleRenderPageSubtitle}
|
||||
{maybeRenderEditViewBtn}
|
||||
|
||||
<If condition={pageSubtitle}>
|
||||
<h3>{ pageSubtitle }</h3>
|
||||
</If>
|
||||
|
||||
<If condition={pageSubtitle && editViewId}>
|
||||
<Button
|
||||
className={Classes.MINIMAL + ' button--view-edit'}
|
||||
icon={<Icon icon="pen" iconSize={13} />}
|
||||
onClick={handlerClickEditView}
|
||||
/>
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<div class='dashboard__breadcrumbs'>
|
||||
<div class="dashboard__breadcrumbs">
|
||||
<DashboardBreadcrumbs />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='dashboard__topbar-right'>
|
||||
<Navbar class='dashboard__topbar-navbar'>
|
||||
<div class="dashboard__topbar-right">
|
||||
<Navbar class="dashboard__topbar-navbar">
|
||||
<NavbarGroup>
|
||||
<Button
|
||||
onClick={() => openGlobalSearch(true)}
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'search-24'} iconSize={20} />}
|
||||
text={<T id={'quick_find'}/>}
|
||||
text={<T id={'quick_find'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'plus-24'} iconSize={20} />}
|
||||
text={<T id={'quick_new'}/>}
|
||||
text={<T id={'quick_new'} />}
|
||||
/>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
@@ -113,13 +117,13 @@ function DashboardTopbar({
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'help-24'} iconSize={20} />}
|
||||
text={<T id={'help'} />} />
|
||||
|
||||
text={<T id={'help'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</NavbarGroup>
|
||||
</Navbar>
|
||||
|
||||
<div class='dashboard__topbar-user'>
|
||||
<div class="dashboard__topbar-user">
|
||||
<DashboardTopbarUser />
|
||||
</div>
|
||||
</div>
|
||||
@@ -129,8 +133,10 @@ function DashboardTopbar({
|
||||
|
||||
export default compose(
|
||||
withSearch,
|
||||
withDashboard(({ pageTitle, pageSubtitle, editViewId }) => ({
|
||||
pageTitle, pageSubtitle, editViewId
|
||||
withDashboard(({ pageTitle, pageSubtitle, editViewId }) => ({
|
||||
pageTitle,
|
||||
pageSubtitle,
|
||||
editViewId,
|
||||
})),
|
||||
withDashboardActions,
|
||||
)(DashboardTopbar);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Button, Tabs, Tab, Tooltip, Position } from '@blueprintjs/core';
|
||||
import { If, Icon } from 'components';
|
||||
|
||||
export default function DashboardViewsTabs({
|
||||
initialViewId = 0,
|
||||
tabs,
|
||||
allTab = true,
|
||||
newViewTab = true,
|
||||
@@ -12,7 +13,7 @@ export default function DashboardViewsTabs({
|
||||
onChange,
|
||||
onTabClick,
|
||||
}) {
|
||||
const [currentView, setCurrentView] = useState(0);
|
||||
const [currentView, setCurrentView] = useState(initialViewId || 0);
|
||||
|
||||
const handleClickNewView = () => {
|
||||
onNewViewTabClick && onNewViewTabClick();
|
||||
|
||||
@@ -45,7 +45,7 @@ export default function DataTable({
|
||||
expandable = false,
|
||||
expandToggleColumn = 2,
|
||||
noInitialFetch = false,
|
||||
spinnerProps = { size: 40 },
|
||||
spinnerProps = { size: 30 },
|
||||
|
||||
pagination = false,
|
||||
pagesCount: controlledPageCount,
|
||||
@@ -282,6 +282,7 @@ export default function DataTable({
|
||||
className={classnames('bigcapital-datatable', className, {
|
||||
'has-sticky': sticky,
|
||||
'is-expandable': expandable,
|
||||
'is-loading': loading,
|
||||
'has-virtualized-rows': virtualizedRows,
|
||||
})}
|
||||
>
|
||||
@@ -357,19 +358,19 @@ export default function DataTable({
|
||||
<div class="td">{noResults}</div>
|
||||
</div>
|
||||
</If>
|
||||
|
||||
<If condition={loading}>
|
||||
<div class="loading">
|
||||
<Spinner size={spinnerProps.size} />
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
|
||||
<If condition={loading}>
|
||||
<div class="loading">
|
||||
<Spinner size={spinnerProps.size} />
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
</ScrollSyncPane>
|
||||
</div>
|
||||
</ScrollSync>
|
||||
|
||||
<If condition={pagination && pageCount}>
|
||||
<If condition={pagination && pageCount && !loading}>
|
||||
<Pagination
|
||||
initialPage={pageIndex + 1}
|
||||
total={pageSize * pageCount}
|
||||
|
||||
@@ -9,8 +9,11 @@ export default function ContactsListCellRenderer({
|
||||
cell: { value: initialValue },
|
||||
payload: { contacts, updateData, errors }
|
||||
}) {
|
||||
const handleContactSelected = useCallback((contactId) => {
|
||||
updateData(index, id, contactId)
|
||||
const handleContactSelected = useCallback((contact) => {
|
||||
updateData(index, {
|
||||
contact_id: contact.id,
|
||||
contact_type: contact.contact_type,
|
||||
});
|
||||
}, [updateData, index, id]);
|
||||
|
||||
const initialContact = useMemo(() => {
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { isDialogOpen, getDialogPayload } from 'store/dashboard/dashboard.selectors';
|
||||
import {
|
||||
isDialogOpenFactory,
|
||||
getDialogPayloadFactory,
|
||||
} from 'store/dashboard/dashboard.selectors';
|
||||
|
||||
export default (Dialog) => {
|
||||
function DialogReduxConnect(props) {
|
||||
return (<Dialog {...props} />);
|
||||
};
|
||||
export default (mapState, dialogName) => {
|
||||
const isDialogOpen = isDialogOpenFactory(dialogName);
|
||||
const getDialogPayload = getDialogPayloadFactory(dialogName);
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
return {
|
||||
const mapped = {
|
||||
dialogName,
|
||||
isOpen: isDialogOpen(state, props),
|
||||
payload: getDialogPayload(state, props),
|
||||
};
|
||||
return mapState ? mapState(mapped) : mapped;
|
||||
};
|
||||
|
||||
return connect(
|
||||
mapStateToProps,
|
||||
)(DialogReduxConnect);
|
||||
}
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
|
||||
@@ -8,13 +8,13 @@ import ExchangeRateDialog from 'containers/Dialogs/ExchangeRateDialog';
|
||||
|
||||
export default function DialogsContainer() {
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<ExchangeRateDialog />
|
||||
<InviteUserDialog />
|
||||
{/* <InviteUserDialog /> */}
|
||||
<CurrencyDialog />
|
||||
<ItemCategoryDialog />
|
||||
<AccountFormDialog />
|
||||
<UserFormDialog />
|
||||
</>
|
||||
{/* <UserFormDialog /> */}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ function DynamicFilterValueField({
|
||||
const [localValue, setLocalValue] = useState();
|
||||
|
||||
const fetchResourceData = useQuery(
|
||||
resourceName && ['resource-data', resourceName],
|
||||
['resource-data', resourceName && resourceName],
|
||||
(k, resName) => requestResourceData(resName),
|
||||
{ manual: true },
|
||||
);
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Tooltip, Position } from '@blueprintjs/core';
|
||||
import Icon from './Icon';
|
||||
|
||||
export default function FieldHint({ hint }) {
|
||||
export default function FieldHint({ content, position }) {
|
||||
return (
|
||||
<span class="hint">
|
||||
<Icon icon="info-circle" iconSize={12} />
|
||||
<Tooltip content={content} position={position}>
|
||||
<Icon icon="info-circle" iconSize={12} />
|
||||
</Tooltip>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
import { FieldRequiredHint } from "components"
|
||||
|
||||
import React from 'react';
|
||||
|
||||
|
||||
export default function FieldRequiredHint() {
|
||||
|
||||
@@ -15,6 +15,9 @@ export default function ListSelect ({
|
||||
|
||||
selectedItem,
|
||||
selectedItemProp = 'id',
|
||||
|
||||
// itemTextProp,
|
||||
// itemLabelProp,
|
||||
...selectProps
|
||||
}) {
|
||||
const [currentItem, setCurrentItem] = useState(null);
|
||||
@@ -29,8 +32,13 @@ export default function ListSelect ({
|
||||
const noResults = isLoading ?
|
||||
('loading') : <MenuItem disabled={true} text={noResultsText} />;
|
||||
|
||||
const itemRenderer = (item, { handleClick, modifiers, query }) => {
|
||||
return (<MenuItem text={item[labelProp]} key={item[selectedItemProp]} />);
|
||||
};
|
||||
|
||||
return (
|
||||
<Select
|
||||
itemRenderer={itemRenderer}
|
||||
{...selectProps}
|
||||
noResults={noResults}
|
||||
>
|
||||
|
||||
@@ -13,6 +13,11 @@ import FieldHint from './FieldHint';
|
||||
import MenuItemLabel from './MenuItemLabel';
|
||||
import Pagination from './Pagination';
|
||||
import DashboardViewsTabs from './Dashboard/DashboardViewsTabs';
|
||||
import CurrenciesSelectList from './CurrenciesSelectList';
|
||||
import FieldRequiredHint from './FieldRequiredHint';
|
||||
import Dialog from './Dialog';
|
||||
import AppToaster from './AppToaster';
|
||||
import DataTable from './DataTable';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
@@ -33,5 +38,9 @@ export {
|
||||
MenuItemLabel,
|
||||
Pagination,
|
||||
DashboardViewsTabs,
|
||||
// For,
|
||||
CurrenciesSelectList,
|
||||
FieldRequiredHint,
|
||||
Dialog,
|
||||
AppToaster,
|
||||
DataTable,
|
||||
};
|
||||
Reference in New Issue
Block a user