mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-20 14:50:32 +00:00
feat: apply new cards design system.
feat: empty status datatables. fix: edit account.
This commit is contained in:
@@ -42,7 +42,7 @@
|
|||||||
"eslint-plugin-react-hooks": "^1.6.1",
|
"eslint-plugin-react-hooks": "^1.6.1",
|
||||||
"file-loader": "4.3.0",
|
"file-loader": "4.3.0",
|
||||||
"flow-bin": "^0.123.0",
|
"flow-bin": "^0.123.0",
|
||||||
"formik": "^2.1.4",
|
"formik": "^2.2.5",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
"html-webpack-plugin": "4.0.0-beta.11",
|
"html-webpack-plugin": "4.0.0-beta.11",
|
||||||
"identity-obj-proxy": "3.0.0",
|
"identity-obj-proxy": "3.0.0",
|
||||||
@@ -70,6 +70,7 @@
|
|||||||
"react-dev-utils": "^10.2.0",
|
"react-dev-utils": "^10.2.0",
|
||||||
"react-dom": "^16.12.0",
|
"react-dom": "^16.12.0",
|
||||||
"react-dropzone": "^11.0.1",
|
"react-dropzone": "^11.0.1",
|
||||||
|
"react-error-boundary": "^3.0.2",
|
||||||
"react-grid-system": "^6.2.3",
|
"react-grid-system": "^6.2.3",
|
||||||
"react-hook-form": "^4.9.4",
|
"react-hook-form": "^4.9.4",
|
||||||
"react-intl": "^3.12.0",
|
"react-intl": "^3.12.0",
|
||||||
@@ -130,6 +131,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-flow": "^7.9.0",
|
"@babel/preset-flow": "^7.9.0",
|
||||||
|
"@welldone-software/why-did-you-render": "^6.0.0-rc.1",
|
||||||
"http-proxy-middleware": "^1.0.0",
|
"http-proxy-middleware": "^1.0.0",
|
||||||
"react-query-devtools": "^2.1.1",
|
"react-query-devtools": "^2.1.1",
|
||||||
"redux-devtools": "^3.5.0"
|
"redux-devtools": "^3.5.0"
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { Classes } from '@blueprintjs/core';
|
|||||||
|
|
||||||
const CLASSES = {
|
const CLASSES = {
|
||||||
DASHBOARD_DATATABLE: 'dashboard__datatable',
|
DASHBOARD_DATATABLE: 'dashboard__datatable',
|
||||||
|
DASHBOARD_CARD: 'dashboard__card',
|
||||||
|
DASHBOARD_CARD_PAGE: 'dashboard__card--page',
|
||||||
|
|
||||||
DATATABLE_EDITOR: 'datatable-editor',
|
DATATABLE_EDITOR: 'datatable-editor',
|
||||||
DATATABLE_EDITOR_ACTIONS: 'datatable-editor__actions',
|
DATATABLE_EDITOR_ACTIONS: 'datatable-editor__actions',
|
||||||
DATATABLE_EDITOR_ITEMS_ENTRIES: 'items-entries-table',
|
DATATABLE_EDITOR_ITEMS_ENTRIES: 'items-entries-table',
|
||||||
|
|||||||
17
client/src/components/Dashboard/DashboardCard.js
Normal file
17
client/src/components/Dashboard/DashboardCard.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
|
// Dashboard card.
|
||||||
|
export default function DashboardCard({ children, page }) {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(CLASSES.DASHBOARD_CARD, {
|
||||||
|
[CLASSES.DASHBOARD_CARD_PAGE]: page,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,15 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { ErrorBoundary } from 'react-error-boundary';
|
||||||
import DashboardTopbar from 'components/Dashboard/DashboardTopbar';
|
import DashboardTopbar from 'components/Dashboard/DashboardTopbar';
|
||||||
import DashboardContentRoute from 'components/Dashboard/DashboardContentRoute';
|
import DashboardContentRoute from 'components/Dashboard/DashboardContentRoute';
|
||||||
import DashboardFooter from 'components/Dashboard/DashboardFooter';
|
import DashboardFooter from 'components/Dashboard/DashboardFooter';
|
||||||
|
import DashboardErrorBoundary from './DashboardErrorBoundary';
|
||||||
|
|
||||||
export default function() {
|
export default function () {
|
||||||
return (
|
return (
|
||||||
|
<ErrorBoundary FallbackComponent={DashboardErrorBoundary}>
|
||||||
<div className="dashboard-content" id="dashboard">
|
<div className="dashboard-content" id="dashboard">
|
||||||
<DashboardTopbar />
|
<DashboardTopbar />
|
||||||
<DashboardContentRoute />
|
<DashboardContentRoute />
|
||||||
|
|
||||||
<DashboardFooter />
|
<DashboardFooter />
|
||||||
</div>
|
</div>
|
||||||
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
12
client/src/components/Dashboard/DashboardErrorBoundary.js
Normal file
12
client/src/components/Dashboard/DashboardErrorBoundary.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Icon } from 'components';
|
||||||
|
|
||||||
|
export default function DashboardErrorBoundary({}) {
|
||||||
|
return (
|
||||||
|
<div class="dashboard__error-boundary">
|
||||||
|
<h1>Sorry about that! Something went wrong</h1>
|
||||||
|
<p>If the problem stuck, please <a href="#">contact us</a> as soon as possible.</p>
|
||||||
|
<Icon icon="bigcapital" height={30} width={160} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { connect } from 'react-redux';
|
|
||||||
import { useHistory } from 'react-router';
|
import { useHistory } from 'react-router';
|
||||||
import {
|
import {
|
||||||
Navbar,
|
Navbar,
|
||||||
@@ -14,7 +13,7 @@ import { FormattedMessage as T } from 'react-intl';
|
|||||||
|
|
||||||
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
|
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
|
||||||
import DashboardBreadcrumbs from 'components/Dashboard/DashboardBreadcrumbs';
|
import DashboardBreadcrumbs from 'components/Dashboard/DashboardBreadcrumbs';
|
||||||
import { Icon, If } from 'components';
|
import { Icon, Hint, If } from 'components';
|
||||||
|
|
||||||
import withSearch from 'containers/GeneralSearch/withSearch';
|
import withSearch from 'containers/GeneralSearch/withSearch';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
@@ -78,8 +77,18 @@ function DashboardTopbar({
|
|||||||
<div class="dashboard__title">
|
<div class="dashboard__title">
|
||||||
<h1>{pageTitle}</h1>
|
<h1>{pageTitle}</h1>
|
||||||
|
|
||||||
|
<If condition={true}>
|
||||||
|
<div class="dashboard__hint">
|
||||||
|
<Hint
|
||||||
|
content={
|
||||||
|
'It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</If>
|
||||||
|
|
||||||
<If condition={pageSubtitle}>
|
<If condition={pageSubtitle}>
|
||||||
<h3>{ pageSubtitle }</h3>
|
<h3>{pageSubtitle}</h3>
|
||||||
</If>
|
</If>
|
||||||
|
|
||||||
<If condition={pageSubtitle && editViewId}>
|
<If condition={pageSubtitle && editViewId}>
|
||||||
|
|||||||
@@ -343,6 +343,7 @@ export default function DataTable({
|
|||||||
<div
|
<div
|
||||||
className={classnames('bigcapital-datatable', className, {
|
className={classnames('bigcapital-datatable', className, {
|
||||||
'has-sticky': sticky,
|
'has-sticky': sticky,
|
||||||
|
'has-pagination': pagination,
|
||||||
'is-expandable': expandable,
|
'is-expandable': expandable,
|
||||||
'is-loading': loading,
|
'is-loading': loading,
|
||||||
'has-virtualized-rows': virtualizedRows,
|
'has-virtualized-rows': virtualizedRows,
|
||||||
|
|||||||
@@ -2,11 +2,15 @@ import React from 'react';
|
|||||||
import { Tooltip, Position } from '@blueprintjs/core';
|
import { Tooltip, Position } from '@blueprintjs/core';
|
||||||
import Icon from './Icon';
|
import Icon from './Icon';
|
||||||
|
|
||||||
export default function FieldHint({ content, position }) {
|
export default function FieldHint({
|
||||||
|
content,
|
||||||
|
position,
|
||||||
|
iconSize = 12
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<span class="hint">
|
<span class="hint">
|
||||||
<Tooltip content={content} position={position}>
|
<Tooltip content={content} position={position}>
|
||||||
<Icon icon="info-circle" iconSize={12} />
|
<Icon icon="info-circle" iconSize={iconSize} />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
|
|||||||
27
client/src/components/Forms/Checkbox.tsx
Normal file
27
client/src/components/Forms/Checkbox.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import {
|
||||||
|
Checkbox as BPCheckbox,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
|
||||||
|
export default function CheckboxComponent(props) {
|
||||||
|
const { field, form, ...rest } = props;
|
||||||
|
const [value, setValue] = useState(field.value || false);
|
||||||
|
|
||||||
|
const handleChange = () => {
|
||||||
|
const checked = !value;
|
||||||
|
form.setFieldValue(field.name, checked);
|
||||||
|
setValue(checked);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleBlur = () => {
|
||||||
|
form.setFieldTouched(field.name);
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkboxProps = {
|
||||||
|
...rest,
|
||||||
|
onChange: handleChange,
|
||||||
|
onBlur: handleBlur,
|
||||||
|
checked: value,
|
||||||
|
}
|
||||||
|
return <BPCheckbox {...checkboxProps} />;
|
||||||
|
}
|
||||||
@@ -38,6 +38,7 @@ import DisplayNameList from './DisplayNameList';
|
|||||||
import MoneyInputGroup from './MoneyInputGroup';
|
import MoneyInputGroup from './MoneyInputGroup';
|
||||||
import Dragzone from './Dragzone';
|
import Dragzone from './Dragzone';
|
||||||
import EmptyStatus from './EmptyStatus';
|
import EmptyStatus from './EmptyStatus';
|
||||||
|
import DashboardCard from './Dashboard/DashboardCard';
|
||||||
|
|
||||||
const Hint = FieldHint;
|
const Hint = FieldHint;
|
||||||
|
|
||||||
@@ -81,5 +82,6 @@ export {
|
|||||||
SalutationList,
|
SalutationList,
|
||||||
MoneyInputGroup,
|
MoneyInputGroup,
|
||||||
Dragzone,
|
Dragzone,
|
||||||
EmptyStatus
|
EmptyStatus,
|
||||||
|
DashboardCard,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -444,7 +444,6 @@ function MakeJournalEntriesForm({
|
|||||||
},
|
},
|
||||||
[changePageSubtitle],
|
[changePageSubtitle],
|
||||||
);
|
);
|
||||||
console.log(values, 'Val');
|
|
||||||
return (
|
return (
|
||||||
<div class="make-journal-entries">
|
<div class="make-journal-entries">
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Intent,
|
Intent,
|
||||||
Button,
|
Button,
|
||||||
Classes,
|
|
||||||
Popover,
|
Popover,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
MenuDivider,
|
MenuDivider,
|
||||||
Position,
|
Position,
|
||||||
Tag,
|
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { withRouter, useParams } from 'react-router-dom';
|
import { withRouter, useParams } from 'react-router-dom';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DataTable,
|
DataTable,
|
||||||
@@ -23,9 +22,11 @@ import {
|
|||||||
Icon,
|
Icon,
|
||||||
LoadingIndicator,
|
LoadingIndicator,
|
||||||
} from 'components';
|
} from 'components';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import { useIsValuePassed } from 'hooks';
|
import { useIsValuePassed } from 'hooks';
|
||||||
|
|
||||||
import ManualJournalsEmptyStatus from './ManualJournalsEmptyStatus';
|
import ManualJournalsEmptyStatus from './ManualJournalsEmptyStatus';
|
||||||
|
import { AmountPopoverContent, NoteAccessor, StatusAccessor } from './components';
|
||||||
|
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
import withManualJournals from 'containers/Accounting/withManualJournals';
|
import withManualJournals from 'containers/Accounting/withManualJournals';
|
||||||
@@ -33,44 +34,7 @@ import withManualJournalsActions from 'containers/Accounting/withManualJournalsA
|
|||||||
|
|
||||||
import { compose, saveInvoke } from 'utils';
|
import { compose, saveInvoke } from 'utils';
|
||||||
|
|
||||||
/**
|
|
||||||
* Status column accessor.
|
|
||||||
*/
|
|
||||||
const StatusAccessor = (row) => {
|
|
||||||
return (
|
|
||||||
<Choose>
|
|
||||||
<Choose.When condition={!!row.status}>
|
|
||||||
<Tag minimal={true}>
|
|
||||||
<T id={'published'} />
|
|
||||||
</Tag>
|
|
||||||
</Choose.When>
|
|
||||||
|
|
||||||
<Choose.Otherwise>
|
|
||||||
<Tag minimal={true} intent={Intent.WARNING}>
|
|
||||||
<T id={'draft'} />
|
|
||||||
</Tag>
|
|
||||||
</Choose.Otherwise>
|
|
||||||
</Choose>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Note column accessor.
|
|
||||||
*/
|
|
||||||
function NoteAccessor(row) {
|
|
||||||
return (
|
|
||||||
<If condition={row.description}>
|
|
||||||
<Tooltip
|
|
||||||
className={Classes.TOOLTIP_INDICATOR}
|
|
||||||
content={row.description}
|
|
||||||
position={Position.LEFT_TOP}
|
|
||||||
hoverOpenDelay={50}
|
|
||||||
>
|
|
||||||
<Icon icon={'file-alt'} iconSize={16} />
|
|
||||||
</Tooltip>
|
|
||||||
</If>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function ManualJournalsDataTable({
|
function ManualJournalsDataTable({
|
||||||
// #withManualJournals
|
// #withManualJournals
|
||||||
@@ -166,7 +130,14 @@ function ManualJournalsDataTable({
|
|||||||
{
|
{
|
||||||
id: 'amount',
|
id: 'amount',
|
||||||
Header: formatMessage({ id: 'amount' }),
|
Header: formatMessage({ id: 'amount' }),
|
||||||
accessor: (r) => <Money amount={r.amount} currency={'USD'} />,
|
accessor: (r) => (
|
||||||
|
<Tooltip
|
||||||
|
content={<AmountPopoverContent journalEntries={r.entries} />}
|
||||||
|
position={Position.RIGHT_BOTTOM}
|
||||||
|
>
|
||||||
|
<Money amount={r.amount} currency={'USD'} />
|
||||||
|
</Tooltip>
|
||||||
|
),
|
||||||
className: 'amount',
|
className: 'amount',
|
||||||
width: 115,
|
width: 115,
|
||||||
},
|
},
|
||||||
@@ -254,9 +225,10 @@ function ManualJournalsDataTable({
|
|||||||
const showEmptyStatus = [
|
const showEmptyStatus = [
|
||||||
manualJournalsCurrentViewId === -1,
|
manualJournalsCurrentViewId === -1,
|
||||||
manualJournalsCurrentPage.length === 0,
|
manualJournalsCurrentPage.length === 0,
|
||||||
].every(condition => condition === true);
|
].every((condition) => condition === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<LoadingIndicator loading={manualJournalsLoading && !isLoadedBefore}>
|
<LoadingIndicator loading={manualJournalsLoading && !isLoadedBefore}>
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={showEmptyStatus}>
|
<Choose.When condition={showEmptyStatus}>
|
||||||
@@ -285,6 +257,7 @@ function ManualJournalsDataTable({
|
|||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -304,7 +277,7 @@ export default compose(
|
|||||||
manualJournalsLoading,
|
manualJournalsLoading,
|
||||||
manualJournalsPagination,
|
manualJournalsPagination,
|
||||||
manualJournalsTableQuery,
|
manualJournalsTableQuery,
|
||||||
manualJournalsCurrentViewId
|
manualJournalsCurrentViewId,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)(ManualJournalsDataTable);
|
)(ManualJournalsDataTable);
|
||||||
|
|||||||
102
client/src/containers/Accounting/components.js
Normal file
102
client/src/containers/Accounting/components.js
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
Intent,
|
||||||
|
Classes,
|
||||||
|
Tooltip,
|
||||||
|
Position,
|
||||||
|
Tag,
|
||||||
|
} from '@blueprintjs/core';
|
||||||
|
import { FormattedMessage as T } from 'react-intl';
|
||||||
|
import { Choose, Money, If, Icon } from 'components';
|
||||||
|
import withAccountDetails from 'containers/Accounts/withAccountDetail';
|
||||||
|
import { compose } from 'utils';
|
||||||
|
|
||||||
|
const AmountPopoverContentLineRender = ({
|
||||||
|
journalEntry,
|
||||||
|
accountId,
|
||||||
|
|
||||||
|
// #withAccountDetail
|
||||||
|
account,
|
||||||
|
}) => {
|
||||||
|
const isCredit = !!journalEntry.credit;
|
||||||
|
const isDebit = !!journalEntry.debit;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Choose>
|
||||||
|
<Choose.When condition={isDebit}>
|
||||||
|
<div>
|
||||||
|
C. <Money amount={journalEntry.debit} currency={'USD'} /> USD -{' '}
|
||||||
|
{account.name} <If condition={account.code}>({account.code})</If>
|
||||||
|
</div>
|
||||||
|
</Choose.When>
|
||||||
|
|
||||||
|
<Choose.When condition={isCredit}>
|
||||||
|
<div class={'ml1'}>
|
||||||
|
D. <Money amount={journalEntry.credit} currency={'USD'} /> USD -{' '}
|
||||||
|
{account.name} <If condition={account.code}>({account.code})</If>
|
||||||
|
</div>
|
||||||
|
</Choose.When>
|
||||||
|
</Choose>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const AmountPopoverContentLine = compose(withAccountDetails)(
|
||||||
|
AmountPopoverContentLineRender,
|
||||||
|
);
|
||||||
|
|
||||||
|
export function AmountPopoverContent({ journalEntries }) {
|
||||||
|
const journalLinesProps = journalEntries.map((journalEntry) => ({
|
||||||
|
journalEntry,
|
||||||
|
accountId: journalEntry.account_id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{journalLinesProps.map(({ journalEntry, accountId }) => (
|
||||||
|
<AmountPopoverContentLine
|
||||||
|
journalEntry={journalEntry}
|
||||||
|
accountId={accountId}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Status column accessor.
|
||||||
|
*/
|
||||||
|
export const StatusAccessor = (row) => {
|
||||||
|
return (
|
||||||
|
<Choose>
|
||||||
|
<Choose.When condition={!!row.status}>
|
||||||
|
<Tag minimal={true}>
|
||||||
|
<T id={'published'} />
|
||||||
|
</Tag>
|
||||||
|
</Choose.When>
|
||||||
|
|
||||||
|
<Choose.Otherwise>
|
||||||
|
<Tag minimal={true} intent={Intent.WARNING}>
|
||||||
|
<T id={'draft'} />
|
||||||
|
</Tag>
|
||||||
|
</Choose.Otherwise>
|
||||||
|
</Choose>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Note column accessor.
|
||||||
|
*/
|
||||||
|
export function NoteAccessor(row) {
|
||||||
|
return (
|
||||||
|
<If condition={row.description}>
|
||||||
|
<Tooltip
|
||||||
|
className={Classes.TOOLTIP_INDICATOR}
|
||||||
|
content={row.description}
|
||||||
|
position={Position.LEFT_TOP}
|
||||||
|
hoverOpenDelay={50}
|
||||||
|
>
|
||||||
|
<Icon icon={'file-alt'} iconSize={16} />
|
||||||
|
</Tooltip>
|
||||||
|
</If>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -12,17 +12,13 @@ import {
|
|||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import classnames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import {
|
import { Icon, DataTable, Money, If, Choose } from 'components';
|
||||||
Icon,
|
|
||||||
DataTable,
|
|
||||||
Money,
|
|
||||||
If,
|
|
||||||
Choose,
|
|
||||||
} from 'components';
|
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import { useUpdateEffect } from 'hooks';
|
import { useUpdateEffect } from 'hooks';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
import withAccountsActions from 'containers/Accounts/withAccountsActions';
|
||||||
import withAccounts from 'containers/Accounts/withAccounts';
|
import withAccounts from 'containers/Accounts/withAccounts';
|
||||||
@@ -30,6 +26,7 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
|||||||
import withCurrentView from 'containers/Views/withCurrentView';
|
import withCurrentView from 'containers/Views/withCurrentView';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function NormalCell({ cell }) {
|
function NormalCell({ cell }) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
@@ -52,7 +49,7 @@ function NormalCell({ cell }) {
|
|||||||
function BalanceCell({ cell }) {
|
function BalanceCell({ cell }) {
|
||||||
const account = cell.row.original;
|
const account = cell.row.original;
|
||||||
|
|
||||||
return (account.amount) ? (
|
return account.amount ? (
|
||||||
<span>
|
<span>
|
||||||
<Money amount={account.amount} currency={'USD'} />
|
<Money amount={account.amount} currency={'USD'} />
|
||||||
</span>
|
</span>
|
||||||
@@ -64,13 +61,14 @@ function BalanceCell({ cell }) {
|
|||||||
function InactiveSemafro() {
|
function InactiveSemafro() {
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={<T id='inactive' />}
|
content={<T id="inactive" />}
|
||||||
className={classnames(
|
className={classNames(
|
||||||
Classes.TOOLTIP_INDICATOR,
|
Classes.TOOLTIP_INDICATOR,
|
||||||
'bp3-popover-wrapper--inactive-semafro'
|
'bp3-popover-wrapper--inactive-semafro',
|
||||||
)}
|
)}
|
||||||
position={Position.TOP}
|
position={Position.TOP}
|
||||||
hoverOpenDelay={250}>
|
hoverOpenDelay={250}
|
||||||
|
>
|
||||||
<div className="inactive-semafro"></div>
|
<div className="inactive-semafro"></div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
);
|
);
|
||||||
@@ -82,7 +80,7 @@ function AccountNameAccessor(row) {
|
|||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={!!row.description}>
|
<Choose.When condition={!!row.description}>
|
||||||
<Tooltip
|
<Tooltip
|
||||||
className={classnames(
|
className={classNames(
|
||||||
Classes.TOOLTIP_INDICATOR,
|
Classes.TOOLTIP_INDICATOR,
|
||||||
'bp3-popover-wrapper--account-desc',
|
'bp3-popover-wrapper--account-desc',
|
||||||
)}
|
)}
|
||||||
@@ -94,9 +92,7 @@ function AccountNameAccessor(row) {
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Choose.When>
|
</Choose.When>
|
||||||
|
|
||||||
<Choose.Otherwise>
|
<Choose.Otherwise>{row.name}</Choose.Otherwise>
|
||||||
{ row.name }
|
|
||||||
</Choose.Otherwise>
|
|
||||||
</Choose>
|
</Choose>
|
||||||
|
|
||||||
<If condition={!row.active}>
|
<If condition={!row.active}>
|
||||||
@@ -159,7 +155,8 @@ function AccountsDataTable({
|
|||||||
<Menu>
|
<Menu>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon="reader-18" />}
|
icon={<Icon icon="reader-18" />}
|
||||||
text={formatMessage({ id: 'view_details' })} />
|
text={formatMessage({ id: 'view_details' })}
|
||||||
|
/>
|
||||||
<MenuDivider />
|
<MenuDivider />
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={<Icon icon="pen-18" />}
|
icon={<Icon icon="pen-18" />}
|
||||||
@@ -287,6 +284,7 @@ function AccountsDataTable({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<DataTable
|
<DataTable
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -301,6 +299,7 @@ function AccountsDataTable({
|
|||||||
rowContextMenu={rowContextMenu}
|
rowContextMenu={rowContextMenu}
|
||||||
expandColumnSpace={1}
|
expandColumnSpace={1}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useCallback } from 'react';
|
|||||||
import { useParams, useHistory } from 'react-router-dom';
|
import { useParams, useHistory } from 'react-router-dom';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
|
import { DashboardCard } from 'components';
|
||||||
import CustomerForm from 'containers/Customers/CustomerForm';
|
import CustomerForm from 'containers/Customers/CustomerForm';
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
|
||||||
@@ -40,12 +41,7 @@ function Customer({
|
|||||||
requestFetchCurrencies(),
|
requestFetchCurrencies(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFormSubmit = useCallback(
|
const handleFormSubmit = useCallback((payload) => {}, [history]);
|
||||||
(payload) => {
|
|
||||||
|
|
||||||
},
|
|
||||||
[history],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleCancel = useCallback(() => {
|
const handleCancel = useCallback(() => {
|
||||||
history.goBack();
|
history.goBack();
|
||||||
@@ -60,11 +56,13 @@ function Customer({
|
|||||||
}
|
}
|
||||||
name={'customer-form'}
|
name={'customer-form'}
|
||||||
>
|
>
|
||||||
|
<DashboardCard page>
|
||||||
<CustomerForm
|
<CustomerForm
|
||||||
onFormSubmit={handleFormSubmit}
|
onFormSubmit={handleFormSubmit}
|
||||||
customerId={id}
|
customerId={id}
|
||||||
onCancelForm={handleCancel}
|
onCancelForm={handleCancel}
|
||||||
/>
|
/>
|
||||||
|
</DashboardCard>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,9 +172,9 @@ function CustomerForm({
|
|||||||
const onSuccess = () => {
|
const onSuccess = () => {
|
||||||
AppToaster.show({
|
AppToaster.show({
|
||||||
message: formatMessage({
|
message: formatMessage({
|
||||||
id: customer ?
|
id: customer
|
||||||
'the_item_customer_has_been_successfully_edited' :
|
? 'the_item_customer_has_been_successfully_edited'
|
||||||
'the_customer_has_been_successfully_created',
|
: 'the_customer_has_been_successfully_created',
|
||||||
}),
|
}),
|
||||||
intent: Intent.SUCCESS,
|
intent: Intent.SUCCESS,
|
||||||
});
|
});
|
||||||
@@ -191,7 +191,9 @@ function CustomerForm({
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (customer && customer.id) {
|
if (customer && customer.id) {
|
||||||
requestEditCustomer(customer.id, formValues).then(onSuccess).catch(onError);
|
requestEditCustomer(customer.id, formValues)
|
||||||
|
.then(onSuccess)
|
||||||
|
.catch(onError);
|
||||||
} else {
|
} else {
|
||||||
requestSubmitCustomer(formValues).then(onSuccess).catch(onError);
|
requestSubmitCustomer(formValues).then(onSuccess).catch(onError);
|
||||||
}
|
}
|
||||||
@@ -239,7 +241,6 @@ function CustomerForm({
|
|||||||
>
|
>
|
||||||
{({ isSubmitting }) => (
|
{({ isSubmitting }) => (
|
||||||
<Form>
|
<Form>
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_HEADER)}>
|
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||||
<CustomerFormPrimarySection />
|
<CustomerFormPrimarySection />
|
||||||
</div>
|
</div>
|
||||||
@@ -247,7 +248,6 @@ function CustomerForm({
|
|||||||
<div className={'page-form__after-priamry-section'}>
|
<div className={'page-form__after-priamry-section'}>
|
||||||
<CustomerFormAfterPrimarySection />
|
<CustomerFormAfterPrimarySection />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
<div className={classNames(CLASSES.PAGE_FORM_TABS)}>
|
||||||
<CustomersTabs customerId={customerId} />
|
<CustomersTabs customerId={customerId} />
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import {
|
|||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import { useIsValuePassed } from 'hooks';
|
import { useIsValuePassed } from 'hooks';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import CustomersEmptyStatus from './CustomersEmptyStatus';
|
import CustomersEmptyStatus from './CustomersEmptyStatus';
|
||||||
import { DataTable, Icon, Money, Choose, LoadingIndicator } from 'components';
|
import { DataTable, Icon, Money, Choose, LoadingIndicator } from 'components';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import withCustomers from './withCustomers';
|
import withCustomers from './withCustomers';
|
||||||
import withCustomersActions from './withCustomersActions';
|
import withCustomersActions from './withCustomersActions';
|
||||||
@@ -186,13 +188,11 @@ const CustomerTable = ({
|
|||||||
const showEmptyStatus = [
|
const showEmptyStatus = [
|
||||||
customersCurrentViewId === -1,
|
customersCurrentViewId === -1,
|
||||||
customers.length === 0,
|
customers.length === 0,
|
||||||
].every(condition => condition === true);
|
].every((condition) => condition === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingIndicator
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
loading={customersLoading && !isLoadedBefore}
|
<LoadingIndicator loading={customersLoading && !isLoadedBefore}>
|
||||||
mount={false}
|
|
||||||
>
|
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={showEmptyStatus}>
|
<Choose.When condition={showEmptyStatus}>
|
||||||
<CustomersEmptyStatus />
|
<CustomersEmptyStatus />
|
||||||
@@ -222,6 +222,7 @@ const CustomerTable = ({
|
|||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -238,7 +239,7 @@ export default compose(
|
|||||||
customersLoading,
|
customersLoading,
|
||||||
customerPagination,
|
customerPagination,
|
||||||
customersTableQuery,
|
customersTableQuery,
|
||||||
customersCurrentViewId
|
customersCurrentViewId,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
withCustomersActions,
|
withCustomersActions,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export default (mapState) => {
|
|||||||
const mapped = {
|
const mapped = {
|
||||||
pageTitle: state.dashboard.pageTitle,
|
pageTitle: state.dashboard.pageTitle,
|
||||||
pageSubtitle: state.dashboard.pageSubtitle,
|
pageSubtitle: state.dashboard.pageSubtitle,
|
||||||
|
pageHint: state.dashboard.pageHint,
|
||||||
editViewId: state.dashboard.topbarEditViewId,
|
editViewId: state.dashboard.topbarEditViewId,
|
||||||
sidebarExpended: state.dashboard.sidebarExpended,
|
sidebarExpended: state.dashboard.sidebarExpended,
|
||||||
preferencesPageTitle: state.dashboard.preferencesPageTitle,
|
preferencesPageTitle: state.dashboard.preferencesPageTitle,
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ const mapActionsToProps = (dispatch) => ({
|
|||||||
pageSubtitle,
|
pageSubtitle,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
changePageHint: (pageHint) =>
|
||||||
|
dispatch({
|
||||||
|
type: t.CHANGE_DASHBOARD_PAGE_HINT,
|
||||||
|
payload: { pageHint }
|
||||||
|
}),
|
||||||
|
|
||||||
setTopbarEditView: (id) =>
|
setTopbarEditView: (id) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.SET_TOPBAR_EDIT_VIEW,
|
type: t.SET_TOPBAR_EDIT_VIEW,
|
||||||
|
|||||||
@@ -88,6 +88,9 @@ function AccountFormDialogContent({
|
|||||||
if (errors.find((e) => e.type === 'NOT_UNIQUE_CODE')) {
|
if (errors.find((e) => e.type === 'NOT_UNIQUE_CODE')) {
|
||||||
fields.code = formatMessage({ id: 'account_code_is_not_unique' });
|
fields.code = formatMessage({ id: 'account_code_is_not_unique' });
|
||||||
}
|
}
|
||||||
|
if (errors.find((e) => e.type === 'ACCOUNT.NAME.NOT.UNIQUE')) {
|
||||||
|
fields.name = formatMessage({ id: 'account_name_is_already_used' });
|
||||||
|
}
|
||||||
return fields;
|
return fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import {
|
|||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import { DataTable, Icon, MoneyExchangeRate } from 'components';
|
import { DataTable, Icon, MoneyExchangeRate } from 'components';
|
||||||
import LoadingIndicator from 'components/LoadingIndicator';
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
@@ -140,6 +143,7 @@ function ExchangeRateTable({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<LoadingIndicator loading={loading} mount={false}>
|
<LoadingIndicator loading={loading} mount={false}>
|
||||||
<DataTable
|
<DataTable
|
||||||
columns={columns}
|
columns={columns}
|
||||||
@@ -158,6 +162,7 @@ function ExchangeRateTable({
|
|||||||
initialPageIndex={exchangeRatesPageination.page - 1}
|
initialPageIndex={exchangeRatesPageination.page - 1}
|
||||||
/>
|
/>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
import React, { useEffect, useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Intent,
|
Intent,
|
||||||
Button,
|
Button,
|
||||||
@@ -15,12 +15,15 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import { compose, saveInvoke } from 'utils';
|
import { compose, saveInvoke } from 'utils';
|
||||||
import { useIsValuePassed } from 'hooks';
|
import { useIsValuePassed } from 'hooks';
|
||||||
|
|
||||||
import { If, Money, Choose, LoadingIndicator } from 'components';
|
import { If, Money, Choose, LoadingIndicator } from 'components';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
import ExpensesEmptyStatus from './ExpensesEmptyStatus';
|
import ExpensesEmptyStatus from './ExpensesEmptyStatus';
|
||||||
|
|
||||||
@@ -268,14 +271,12 @@ function ExpensesDataTable({
|
|||||||
|
|
||||||
const showEmptyStatus = [
|
const showEmptyStatus = [
|
||||||
expensesCurrentViewId === -1,
|
expensesCurrentViewId === -1,
|
||||||
expensesCurrentPage.length === 0
|
expensesCurrentPage.length === 0,
|
||||||
].every(condition => condition === true);
|
].every((condition) => condition === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<LoadingIndicator
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
loading={expensesLoading && !isLoadedBefore}
|
<LoadingIndicator loading={expensesLoading && !isLoadedBefore}>
|
||||||
mount={false}
|
|
||||||
>
|
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={showEmptyStatus}>
|
<Choose.When condition={showEmptyStatus}>
|
||||||
<ExpensesEmptyStatus />
|
<ExpensesEmptyStatus />
|
||||||
@@ -302,6 +303,7 @@ function ExpensesDataTable({
|
|||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useIntl } from 'react-intl';
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import { For } from 'components';
|
import { For } from 'components';
|
||||||
|
|
||||||
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
import financialReportMenus from 'config/financialReportsMenu';
|
import financialReportMenus from 'config/financialReportsMenu';
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
|
||||||
@@ -42,9 +43,11 @@ function FinancialReports({
|
|||||||
}, [changePageTitle, formatMessage]);
|
}, [changePageTitle, formatMessage]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<DashboardInsider name={'financial-reports'}>
|
||||||
<div class="financial-reports">
|
<div class="financial-reports">
|
||||||
<For render={FinancialReportsSection} of={financialReportMenus} />
|
<For render={FinancialReportsSection} of={financialReportMenus} />
|
||||||
</div>
|
</div>
|
||||||
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,15 @@ import {
|
|||||||
Position,
|
Position,
|
||||||
} from '@blueprintjs/core';
|
} from '@blueprintjs/core';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import LoadingIndicator from 'components/LoadingIndicator';
|
import LoadingIndicator from 'components/LoadingIndicator';
|
||||||
import { compose } from 'utils';
|
import { compose } from 'utils';
|
||||||
import DataTable from 'components/DataTable';
|
import DataTable from 'components/DataTable';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import withItemCategories from './withItemCategories';
|
import withItemCategories from './withItemCategories';
|
||||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||||
|
|
||||||
@@ -139,6 +142,7 @@ const ItemsCategoryList = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<LoadingIndicator mount={false}>
|
<LoadingIndicator mount={false}>
|
||||||
<DataTable
|
<DataTable
|
||||||
noInitialFetch={true}
|
noInitialFetch={true}
|
||||||
@@ -154,6 +158,7 @@ const ItemsCategoryList = ({
|
|||||||
rowContextMenu={handleRowContextMenu}
|
rowContextMenu={handleRowContextMenu}
|
||||||
/>
|
/>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
import React, { useState, useMemo, useCallback, useEffect } from 'react';
|
||||||
import * as Yup from 'yup';
|
|
||||||
import { Formik, Form } from 'formik';
|
import { Formik, Form } from 'formik';
|
||||||
import { Intent } from '@blueprintjs/core';
|
import { Intent } from '@blueprintjs/core';
|
||||||
import { queryCache } from 'react-query';
|
import { queryCache } from 'react-query';
|
||||||
import { useHistory } from 'react-router-dom';
|
import { useHistory } from 'react-router-dom';
|
||||||
import { useIntl } from 'react-intl';
|
import { useIntl } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
import { defaultTo } from 'lodash';
|
||||||
|
|
||||||
import { CLASSES } from 'common/classes';
|
import { CLASSES } from 'common/classes';
|
||||||
import AppToaster from 'components/AppToaster';
|
import AppToaster from 'components/AppToaster';
|
||||||
@@ -22,6 +22,8 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
|||||||
import withSettings from 'containers/Settings/withSettings';
|
import withSettings from 'containers/Settings/withSettings';
|
||||||
|
|
||||||
import { compose, transformToForm } from 'utils';
|
import { compose, transformToForm } from 'utils';
|
||||||
|
import { transitionItemTypeKeyToLabel } from './utils';
|
||||||
|
import { EditItemFormSchema, CreateItemFormSchema } from './ItemForm.schema';
|
||||||
|
|
||||||
const defaultInitialValues = {
|
const defaultInitialValues = {
|
||||||
active: true,
|
active: true,
|
||||||
@@ -83,66 +85,15 @@ function ItemForm({
|
|||||||
deleteCallback: requestDeleteMedia,
|
deleteCallback: requestDeleteMedia,
|
||||||
});
|
});
|
||||||
|
|
||||||
const validationSchema = Yup.object().shape({
|
|
||||||
active: Yup.boolean(),
|
|
||||||
name: Yup.string()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'item_name_' })),
|
|
||||||
type: Yup.string()
|
|
||||||
.trim()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'item_type_' })),
|
|
||||||
sku: Yup.string().trim(),
|
|
||||||
cost_price: Yup.number().when(['purchasable'], {
|
|
||||||
is: true,
|
|
||||||
then: Yup.number()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'cost_price_' })),
|
|
||||||
otherwise: Yup.number().nullable(true),
|
|
||||||
}),
|
|
||||||
sell_price: Yup.number().when(['sellable'], {
|
|
||||||
is: true,
|
|
||||||
then: Yup.number()
|
|
||||||
.required()
|
|
||||||
.label(formatMessage({ id: 'sell_price_' })),
|
|
||||||
otherwise: Yup.number().nullable(true),
|
|
||||||
}),
|
|
||||||
cost_account_id: Yup.number()
|
|
||||||
.when(['purchasable'], {
|
|
||||||
is: true,
|
|
||||||
then: Yup.number().required(),
|
|
||||||
otherwise: Yup.number().nullable(true),
|
|
||||||
})
|
|
||||||
.label(formatMessage({ id: 'cost_account_id' })),
|
|
||||||
sell_account_id: Yup.number()
|
|
||||||
.when(['sellable'], {
|
|
||||||
is: true,
|
|
||||||
then: Yup.number().required(),
|
|
||||||
otherwise: Yup.number().nullable(),
|
|
||||||
})
|
|
||||||
.label(formatMessage({ id: 'sell_account_id' })),
|
|
||||||
inventory_account_id: Yup.number()
|
|
||||||
.when(['type'], {
|
|
||||||
is: (value) => value === 'inventory',
|
|
||||||
then: Yup.number().required(),
|
|
||||||
otherwise: Yup.number().nullable(),
|
|
||||||
})
|
|
||||||
.label(formatMessage({ id: 'inventory_account' })),
|
|
||||||
category_id: Yup.number().positive().nullable(),
|
|
||||||
stock: Yup.string() || Yup.boolean(),
|
|
||||||
sellable: Yup.boolean().required(),
|
|
||||||
purchasable: Yup.boolean().required(),
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial values in create and edit mode.
|
* Initial values in create and edit mode.
|
||||||
*/
|
*/
|
||||||
const initialValues = useMemo(
|
const initialValues = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
...defaultInitialValues,
|
...defaultInitialValues,
|
||||||
cost_account_id: parseInt(preferredCostAccount),
|
cost_account_id: defaultTo(preferredCostAccount, ''),
|
||||||
sell_account_id: parseInt(preferredSellAccount),
|
sell_account_id: defaultTo(preferredSellAccount, ''),
|
||||||
inventory_account_id: parseInt(preferredInventoryAccount),
|
inventory_account_id: defaultTo(preferredInventoryAccount, ''),
|
||||||
/**
|
/**
|
||||||
* We only care about the fields in the form. Previously unfilled optional
|
* We only care about the fields in the form. Previously unfilled optional
|
||||||
* values such as `notes` come back from the API as null, so remove those
|
* values such as `notes` come back from the API as null, so remove those
|
||||||
@@ -150,7 +101,12 @@ function ItemForm({
|
|||||||
*/
|
*/
|
||||||
...transformToForm(itemDetail, defaultInitialValues),
|
...transformToForm(itemDetail, defaultInitialValues),
|
||||||
}),
|
}),
|
||||||
[],
|
[
|
||||||
|
itemDetail,
|
||||||
|
preferredCostAccount,
|
||||||
|
preferredSellAccount,
|
||||||
|
preferredInventoryAccount,
|
||||||
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -214,7 +170,7 @@ function ItemForm({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (itemDetail && itemDetail.type) {
|
if (itemDetail && itemDetail.type) {
|
||||||
changePageSubtitle(formatMessage({ id: itemDetail.type }));
|
changePageSubtitle(transitionItemTypeKeyToLabel(itemDetail.type));
|
||||||
}
|
}
|
||||||
}, [itemDetail, changePageSubtitle, formatMessage]);
|
}, [itemDetail, changePageSubtitle, formatMessage]);
|
||||||
|
|
||||||
@@ -262,14 +218,14 @@ function ItemForm({
|
|||||||
<div class={classNames(CLASSES.PAGE_FORM_ITEM)}>
|
<div class={classNames(CLASSES.PAGE_FORM_ITEM)}>
|
||||||
<Formik
|
<Formik
|
||||||
enableReinitialize={true}
|
enableReinitialize={true}
|
||||||
validationSchema={validationSchema}
|
validationSchema={isNewMode ? CreateItemFormSchema : EditItemFormSchema}
|
||||||
initialValues={initialValues}
|
initialValues={initialValues}
|
||||||
onSubmit={handleFormSubmit}
|
onSubmit={handleFormSubmit}
|
||||||
>
|
>
|
||||||
{({ isSubmitting, handleSubmit }) => (
|
{({ isSubmitting, handleSubmit }) => (
|
||||||
<Form>
|
<Form>
|
||||||
<div class={classNames(CLASSES.PAGE_FORM_BODY)}>
|
<div class={classNames(CLASSES.PAGE_FORM_BODY)}>
|
||||||
<ItemFormPrimarySection />
|
<ItemFormPrimarySection itemId={itemId} />
|
||||||
<ItemFormBody />
|
<ItemFormBody />
|
||||||
<ItemFormInventorySection />
|
<ItemFormInventorySection />
|
||||||
</div>
|
</div>
|
||||||
@@ -294,8 +250,10 @@ export default compose(
|
|||||||
withDashboardActions,
|
withDashboardActions,
|
||||||
withMediaActions,
|
withMediaActions,
|
||||||
withSettings(({ itemsSettings }) => ({
|
withSettings(({ itemsSettings }) => ({
|
||||||
preferredCostAccount: itemsSettings.preferredCostAccount,
|
preferredCostAccount: parseInt(itemsSettings?.preferredCostAccount),
|
||||||
preferredSellAccount: itemsSettings.preferredSellAccount,
|
preferredSellAccount: parseInt(itemsSettings?.preferredSellAccount),
|
||||||
preferredInventoryAccount: itemsSettings.preferredInventoryAccount,
|
preferredInventoryAccount: parseInt(
|
||||||
|
itemsSettings?.preferredInventoryAccount,
|
||||||
|
),
|
||||||
})),
|
})),
|
||||||
)(ItemForm);
|
)(ItemForm);
|
||||||
|
|||||||
56
client/src/containers/Items/ItemForm.schema.js
Normal file
56
client/src/containers/Items/ItemForm.schema.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import * as Yup from 'yup';
|
||||||
|
import { formatMessage } from 'services/intl';
|
||||||
|
|
||||||
|
const Schema = Yup.object().shape({
|
||||||
|
active: Yup.boolean(),
|
||||||
|
name: Yup.string()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'item_name_' })),
|
||||||
|
type: Yup.string()
|
||||||
|
.trim()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'item_type_' })),
|
||||||
|
sku: Yup.string().trim(),
|
||||||
|
cost_price: Yup.number().when(['purchasable'], {
|
||||||
|
is: true,
|
||||||
|
then: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'cost_price_' })),
|
||||||
|
otherwise: Yup.number().nullable(true),
|
||||||
|
}),
|
||||||
|
sell_price: Yup.number().when(['sellable'], {
|
||||||
|
is: true,
|
||||||
|
then: Yup.number()
|
||||||
|
.required()
|
||||||
|
.label(formatMessage({ id: 'sell_price_' })),
|
||||||
|
otherwise: Yup.number().nullable(true),
|
||||||
|
}),
|
||||||
|
cost_account_id: Yup.number()
|
||||||
|
.when(['purchasable'], {
|
||||||
|
is: true,
|
||||||
|
then: Yup.number().required(),
|
||||||
|
otherwise: Yup.number().nullable(true),
|
||||||
|
})
|
||||||
|
.label(formatMessage({ id: 'cost_account_id' })),
|
||||||
|
sell_account_id: Yup.number()
|
||||||
|
.when(['sellable'], {
|
||||||
|
is: true,
|
||||||
|
then: Yup.number().required(),
|
||||||
|
otherwise: Yup.number().nullable(),
|
||||||
|
})
|
||||||
|
.label(formatMessage({ id: 'sell_account_id' })),
|
||||||
|
inventory_account_id: Yup.number()
|
||||||
|
.when(['type'], {
|
||||||
|
is: (value) => value === 'inventory',
|
||||||
|
then: Yup.number().required(),
|
||||||
|
otherwise: Yup.number().nullable(),
|
||||||
|
})
|
||||||
|
.label(formatMessage({ id: 'inventory_account' })),
|
||||||
|
category_id: Yup.number().positive().nullable(),
|
||||||
|
stock: Yup.string() || Yup.boolean(),
|
||||||
|
sellable: Yup.boolean().required(),
|
||||||
|
purchasable: Yup.boolean().required(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const CreateItemFormSchema = Schema;
|
||||||
|
export const EditItemFormSchema = Schema;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FastField, ErrorMessage } from 'formik';
|
import { FastField, Field, ErrorMessage } from 'formik';
|
||||||
import {
|
import {
|
||||||
FormGroup,
|
FormGroup,
|
||||||
Classes,
|
Classes,
|
||||||
@@ -27,8 +27,8 @@ function ItemFormBody({ accountsList }) {
|
|||||||
<Row>
|
<Row>
|
||||||
<Col xs={6}>
|
<Col xs={6}>
|
||||||
{/*------------- Purchasable checbox ------------- */}
|
{/*------------- Purchasable checbox ------------- */}
|
||||||
<FastField name={'sellable'}>
|
<FastField name={'sellable'} type="checkbox">
|
||||||
{({ field, field: { value } }) => (
|
{({ field: { onChange, onBlur, name, checked } }) => (
|
||||||
<FormGroup inline={true} className={'form-group--sellable'}>
|
<FormGroup inline={true} className={'form-group--sellable'}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
inline={true}
|
inline={true}
|
||||||
@@ -37,8 +37,10 @@ function ItemFormBody({ accountsList }) {
|
|||||||
<T id={'i_sell_this_item'} />
|
<T id={'i_sell_this_item'} />
|
||||||
</h3>
|
</h3>
|
||||||
}
|
}
|
||||||
checked={value}
|
name={'sellable'}
|
||||||
{...field}
|
checked={!!checked}
|
||||||
|
onChange={onChange}
|
||||||
|
onBlur={onBlur}
|
||||||
/>
|
/>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ export default function ItemFormFloatingActions({
|
|||||||
{/*----------- Active ----------*/}
|
{/*----------- Active ----------*/}
|
||||||
<FastField name={'active'}>
|
<FastField name={'active'}>
|
||||||
{({ field, field: { value } }) => (
|
{({ field, field: { value } }) => (
|
||||||
<FormGroup label={' '} inline={true} className={'form-group--active'}>
|
<FormGroup inline={true} className={'form-group--active'}>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
inline={true}
|
inline={true}
|
||||||
label={<T id={'active'} />}
|
label={<T id={'active'} />}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { useParams, useHistory } from 'react-router-dom';
|
|||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
|
|
||||||
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
import DashboardInsider from 'components/Dashboard/DashboardInsider';
|
||||||
|
import DashboardCard from 'components/Dashboard/DashboardCard';
|
||||||
import ItemForm from 'containers/Items/ItemForm';
|
import ItemForm from 'containers/Items/ItemForm';
|
||||||
|
|
||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
@@ -62,11 +63,13 @@ const ItemFormContainer = ({
|
|||||||
}
|
}
|
||||||
name={'item-form'}
|
name={'item-form'}
|
||||||
>
|
>
|
||||||
|
<DashboardCard page>
|
||||||
<ItemForm
|
<ItemForm
|
||||||
onFormSubmit={handleFormSubmit}
|
onFormSubmit={handleFormSubmit}
|
||||||
itemId={id}
|
itemId={id}
|
||||||
onCancelForm={handleCancel}
|
onCancelForm={handleCancel}
|
||||||
/>
|
/>
|
||||||
|
</DashboardCard>
|
||||||
</DashboardInsider>
|
</DashboardInsider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import withAccounts from 'containers/Accounts/withAccounts';
|
|||||||
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||||
|
|
||||||
import { compose, handleStringChange, inputIntent } from 'utils';
|
import { compose, handleStringChange, inputIntent } from 'utils';
|
||||||
|
import { transitionItemTypeKeyToLabel } from './utils';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Item form primary section.
|
* Item form primary section.
|
||||||
@@ -35,8 +36,12 @@ function ItemFormPrimarySection({
|
|||||||
|
|
||||||
// #withDashboardActions
|
// #withDashboardActions
|
||||||
changePageSubtitle,
|
changePageSubtitle,
|
||||||
|
|
||||||
|
// #ownProps
|
||||||
|
itemId,
|
||||||
}) {
|
}) {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const isNewMode = !itemId;
|
||||||
|
|
||||||
const itemTypeHintContent = (
|
const itemTypeHintContent = (
|
||||||
<>
|
<>
|
||||||
@@ -59,8 +64,6 @@ function ItemFormPrimarySection({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
<div className={classNames(CLASSES.PAGE_FORM_HEADER_PRIMARY)}>
|
||||||
<Row>
|
|
||||||
<Col xs={7}>
|
|
||||||
{/*----------- Item type ----------*/}
|
{/*----------- Item type ----------*/}
|
||||||
<FastField name={'type'}>
|
<FastField name={'type'}>
|
||||||
{({ form, field: { value }, meta: { touched, error } }) => (
|
{({ form, field: { value }, meta: { touched, error } }) => (
|
||||||
@@ -85,22 +88,21 @@ function ItemFormPrimarySection({
|
|||||||
inline={true}
|
inline={true}
|
||||||
onChange={handleStringChange((_value) => {
|
onChange={handleStringChange((_value) => {
|
||||||
form.setFieldValue('type', _value);
|
form.setFieldValue('type', _value);
|
||||||
changePageSubtitle(formatMessage({ id: _value }));
|
changePageSubtitle(transitionItemTypeKeyToLabel(_value));
|
||||||
})}
|
})}
|
||||||
selectedValue={value}
|
selectedValue={value}
|
||||||
disabled={value === 'inventory'}
|
disabled={value === 'inventory' && !isNewMode}
|
||||||
>
|
>
|
||||||
<Radio label={<T id={'service'} />} value="service" />
|
<Radio label={<T id={'service'} />} value="service" />
|
||||||
<Radio
|
<Radio label={<T id={'non_inventory'} />} value="non-inventory" />
|
||||||
label={<T id={'non_inventory'} />}
|
|
||||||
value="non-inventory"
|
|
||||||
/>
|
|
||||||
<Radio label={<T id={'inventory'} />} value="inventory" />
|
<Radio label={<T id={'inventory'} />} value="inventory" />
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</FormGroup>
|
</FormGroup>
|
||||||
)}
|
)}
|
||||||
</FastField>
|
</FastField>
|
||||||
|
|
||||||
|
<Row>
|
||||||
|
<Col xs={7}>
|
||||||
{/*----------- Item name ----------*/}
|
{/*----------- Item name ----------*/}
|
||||||
<FastField name={'name'}>
|
<FastField name={'name'}>
|
||||||
{({ field, meta: { error, touched } }) => (
|
{({ field, meta: { error, touched } }) => (
|
||||||
|
|||||||
10
client/src/containers/Items/utils.js
Normal file
10
client/src/containers/Items/utils.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { formatMessage } from "services/intl";
|
||||||
|
|
||||||
|
export const transitionItemTypeKeyToLabel = (itemTypeKey) => {
|
||||||
|
const table = {
|
||||||
|
'service': formatMessage({ id: 'service' }),
|
||||||
|
'inventory': formatMessage({ id: 'inventory' }),
|
||||||
|
'non-inventory': formatMessage({ id: 'non_inventory' }),
|
||||||
|
};
|
||||||
|
return typeof table[itemTypeKey] === 'string' ? table[itemTypeKey] : '';
|
||||||
|
};
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
import React, { useEffect, useCallback, useState, useMemo } from 'react';
|
import React, { useEffect, useCallback, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Intent,
|
Intent,
|
||||||
Button,
|
Button,
|
||||||
Classes,
|
|
||||||
Popover,
|
Popover,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
@@ -14,9 +13,11 @@ import { useParams } from 'react-router-dom';
|
|||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import Icon from 'components/Icon';
|
import Icon from 'components/Icon';
|
||||||
import { compose, saveInvoke } from 'utils';
|
import { compose, saveInvoke } from 'utils';
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import { useIsValuePassed } from 'hooks';
|
import { useIsValuePassed } from 'hooks';
|
||||||
|
|
||||||
import { LoadingIndicator, Choose } from 'components';
|
import { LoadingIndicator, Choose } from 'components';
|
||||||
@@ -221,9 +222,10 @@ function BillsDataTable({
|
|||||||
const showEmptyStatus = [
|
const showEmptyStatus = [
|
||||||
billsCurrentViewId === -1,
|
billsCurrentViewId === -1,
|
||||||
billsCurrentPage.length === 0,
|
billsCurrentPage.length === 0,
|
||||||
].every(condition => condition === true);
|
].every((condition) => condition === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<LoadingIndicator loading={billsLoading && !isLoadedBefore} mount={false}>
|
<LoadingIndicator loading={billsLoading && !isLoadedBefore} mount={false}>
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={showEmptyStatus}>
|
<Choose.When condition={showEmptyStatus}>
|
||||||
@@ -249,6 +251,7 @@ function BillsDataTable({
|
|||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -264,13 +267,13 @@ export default compose(
|
|||||||
billsLoading,
|
billsLoading,
|
||||||
billsPageination,
|
billsPageination,
|
||||||
billsTableQuery,
|
billsTableQuery,
|
||||||
billsCurrentViewId
|
billsCurrentViewId,
|
||||||
}) => ({
|
}) => ({
|
||||||
billsCurrentPage,
|
billsCurrentPage,
|
||||||
billsLoading,
|
billsLoading,
|
||||||
billsPageination,
|
billsPageination,
|
||||||
billsTableQuery,
|
billsTableQuery,
|
||||||
billsCurrentViewId
|
billsCurrentViewId,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
withViewDetails(),
|
withViewDetails(),
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ import {
|
|||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { compose, saveInvoke } from 'utils';
|
import { compose, saveInvoke } from 'utils';
|
||||||
import { useIsValuePassed } from 'hooks';
|
import { useIsValuePassed } from 'hooks';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import { DataTable, Money, Icon, Choose, LoadingIndicator } from 'components';
|
import { DataTable, Money, Icon, Choose, LoadingIndicator } from 'components';
|
||||||
import PaymentMadesEmptyStatus from './PaymentMadesEmptyStatus';
|
import PaymentMadesEmptyStatus from './PaymentMadesEmptyStatus';
|
||||||
|
|
||||||
@@ -182,9 +184,10 @@ function PaymentMadeDataTable({
|
|||||||
const showEmptyStatuts = [
|
const showEmptyStatuts = [
|
||||||
paymentMadeCurrentPage.length === 0,
|
paymentMadeCurrentPage.length === 0,
|
||||||
paymentMadesCurrentViewId === -1,
|
paymentMadesCurrentViewId === -1,
|
||||||
].every(condition => condition === true);
|
].every((condition) => condition === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<LoadingIndicator loading={paymentMadesLoading && !isLoaded}>
|
<LoadingIndicator loading={paymentMadesLoading && !isLoaded}>
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={showEmptyStatuts}>
|
<Choose.When condition={showEmptyStatuts}>
|
||||||
@@ -212,6 +215,7 @@ function PaymentMadeDataTable({
|
|||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,13 +229,13 @@ export default compose(
|
|||||||
paymentMadesLoading,
|
paymentMadesLoading,
|
||||||
paymentMadePageination,
|
paymentMadePageination,
|
||||||
paymentMadeTableQuery,
|
paymentMadeTableQuery,
|
||||||
paymentMadesCurrentViewId
|
paymentMadesCurrentViewId,
|
||||||
}) => ({
|
}) => ({
|
||||||
paymentMadeCurrentPage,
|
paymentMadeCurrentPage,
|
||||||
paymentMadesLoading,
|
paymentMadesLoading,
|
||||||
paymentMadePageination,
|
paymentMadePageination,
|
||||||
paymentMadeTableQuery,
|
paymentMadeTableQuery,
|
||||||
paymentMadesCurrentViewId
|
paymentMadesCurrentViewId,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)(PaymentMadeDataTable);
|
)(PaymentMadeDataTable);
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ function PaymentReceiveFormPage({
|
|||||||
fetchAccounts.isFetching ||
|
fetchAccounts.isFetching ||
|
||||||
// fetchSettings.isFetching ||
|
// fetchSettings.isFetching ||
|
||||||
fetchCustomers.isFetching
|
fetchCustomers.isFetching
|
||||||
}>
|
}
|
||||||
|
name={'payment-receive-form'}
|
||||||
|
>
|
||||||
<PaymentReceiveForm
|
<PaymentReceiveForm
|
||||||
paymentReceiveId={paymentReceiveId}
|
paymentReceiveId={paymentReceiveId}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -11,10 +11,13 @@ import {
|
|||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { compose, saveInvoke } from 'utils';
|
import { compose, saveInvoke } from 'utils';
|
||||||
import { useIsValuePassed } from 'hooks';
|
import { useIsValuePassed } from 'hooks';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
|
|
||||||
import PaymentReceivesEmptyStatus from './PaymentReceivesEmptyStatus';
|
import PaymentReceivesEmptyStatus from './PaymentReceivesEmptyStatus';
|
||||||
import { LoadingIndicator, DataTable, Choose, Money, Icon } from 'components';
|
import { LoadingIndicator, DataTable, Choose, Money, Icon } from 'components';
|
||||||
|
|
||||||
@@ -186,6 +189,7 @@ function PaymentReceivesDataTable({
|
|||||||
].every(condition => condition === true);
|
].every(condition => condition === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<LoadingIndicator
|
<LoadingIndicator
|
||||||
loading={paymentReceivesLoading && !isLoaded}
|
loading={paymentReceivesLoading && !isLoaded}
|
||||||
mount={false}
|
mount={false}
|
||||||
@@ -216,6 +220,7 @@ function PaymentReceivesDataTable({
|
|||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ import {
|
|||||||
import { withRouter } from 'react-router';
|
import { withRouter } from 'react-router';
|
||||||
import { FormattedMessage as T, useIntl } from 'react-intl';
|
import { FormattedMessage as T, useIntl } from 'react-intl';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
import { compose, saveInvoke } from 'utils';
|
import { compose, saveInvoke } from 'utils';
|
||||||
import { useIsValuePassed } from 'hooks';
|
import { useIsValuePassed } from 'hooks';
|
||||||
|
|
||||||
|
import { CLASSES } from 'common/classes';
|
||||||
import { Choose, LoadingIndicator, DataTable, Money, Icon } from 'components';
|
import { Choose, LoadingIndicator, DataTable, Money, Icon } from 'components';
|
||||||
|
|
||||||
import ReceiptsEmptyStatus from './ReceiptsEmptyStatus';
|
import ReceiptsEmptyStatus from './ReceiptsEmptyStatus';
|
||||||
@@ -192,9 +194,10 @@ function ReceiptsDataTable({
|
|||||||
const showEmptyStatus = [
|
const showEmptyStatus = [
|
||||||
receiptsCurrentViewId === -1,
|
receiptsCurrentViewId === -1,
|
||||||
receiptsCurrentPage.length === 0,
|
receiptsCurrentPage.length === 0,
|
||||||
].every(condition => condition === true);
|
].every((condition) => condition === true);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div className={classNames(CLASSES.DASHBOARD_DATATABLE)}>
|
||||||
<LoadingIndicator loading={receiptsLoading && !isLoadedBefore}>
|
<LoadingIndicator loading={receiptsLoading && !isLoadedBefore}>
|
||||||
<Choose>
|
<Choose>
|
||||||
<Choose.When condition={showEmptyStatus}>
|
<Choose.When condition={showEmptyStatus}>
|
||||||
@@ -222,6 +225,7 @@ function ReceiptsDataTable({
|
|||||||
</Choose.Otherwise>
|
</Choose.Otherwise>
|
||||||
</Choose>
|
</Choose>
|
||||||
</LoadingIndicator>
|
</LoadingIndicator>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,13 +240,13 @@ export default compose(
|
|||||||
receiptsLoading,
|
receiptsLoading,
|
||||||
receiptsPagination,
|
receiptsPagination,
|
||||||
receiptTableQuery,
|
receiptTableQuery,
|
||||||
receiptsCurrentViewId
|
receiptsCurrentViewId,
|
||||||
}) => ({
|
}) => ({
|
||||||
receiptsCurrentPage,
|
receiptsCurrentPage,
|
||||||
receiptsLoading,
|
receiptsLoading,
|
||||||
receiptsPagination,
|
receiptsPagination,
|
||||||
receiptTableQuery,
|
receiptTableQuery,
|
||||||
receiptsCurrentViewId
|
receiptsCurrentViewId,
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
)(ReceiptsDataTable);
|
)(ReceiptsDataTable);
|
||||||
|
|||||||
@@ -7,12 +7,17 @@ import App from 'components/App';
|
|||||||
import * as serviceWorker from 'serviceWorker';
|
import * as serviceWorker from 'serviceWorker';
|
||||||
import createStore from 'store/createStore';
|
import createStore from 'store/createStore';
|
||||||
import AppProgress from 'components/NProgress/AppProgress';
|
import AppProgress from 'components/NProgress/AppProgress';
|
||||||
import { setLocale } from 'yup';
|
|
||||||
import {locale} from 'lang/en/locale';
|
import {locale} from 'lang/en/locale';
|
||||||
|
import whyDidYouRender from "@welldone-software/why-did-you-render";
|
||||||
|
|
||||||
|
whyDidYouRender(React, {
|
||||||
|
onlyLogs: true,
|
||||||
|
titleColor: "green",
|
||||||
|
diffNameColor: "aqua"
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setLocale(locale)
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={createStore}>
|
<Provider store={createStore}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
|
|||||||
@@ -213,8 +213,11 @@ export default {
|
|||||||
once_delete_this_item_you_will_able_to_restore_it: `Once you delete this item, you won\'t be able to restore the item later. Are you sure you want to delete ?<br /><br />If you're not sure, you can inactivate it instead.`,
|
once_delete_this_item_you_will_able_to_restore_it: `Once you delete this item, you won\'t be able to restore the item later. Are you sure you want to delete ?<br /><br />If you're not sure, you can inactivate it instead.`,
|
||||||
the_item_has_been_successfully_deleted:
|
the_item_has_been_successfully_deleted:
|
||||||
'The item has been successfully deleted.',
|
'The item has been successfully deleted.',
|
||||||
|
the_item_has_been_created_successfully:
|
||||||
|
'The item has been created successfully.',
|
||||||
the_item_has_been_successfully_edited:
|
the_item_has_been_successfully_edited:
|
||||||
'The item #{number} has been successfully edited.',
|
'The item #{number} has been successfully edited.',
|
||||||
|
|
||||||
the_item_category_has_been_successfully_created:
|
the_item_category_has_been_successfully_created:
|
||||||
'The item category has been successfully created.',
|
'The item category has been successfully created.',
|
||||||
the_item_category_has_been_successfully_edited:
|
the_item_category_has_been_successfully_edited:
|
||||||
@@ -819,4 +822,5 @@ export default {
|
|||||||
the_name_used_before: 'The name is already used.',
|
the_name_used_before: 'The name is already used.',
|
||||||
the_item_has_associated_transactions: 'The item has associated transactions.',
|
the_item_has_associated_transactions: 'The item has associated transactions.',
|
||||||
customer_has_sales_invoices: 'Customer has sales invoices',
|
customer_has_sales_invoices: 'Customer has sales invoices',
|
||||||
|
account_name_is_already_used: 'Account name is already used.',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { createReducer } from '@reduxjs/toolkit';
|
|||||||
const initialState = {
|
const initialState = {
|
||||||
pageTitle: '',
|
pageTitle: '',
|
||||||
pageSubtitle: '',
|
pageSubtitle: '',
|
||||||
|
pageHint: '',
|
||||||
preferencesPageTitle: '',
|
preferencesPageTitle: '',
|
||||||
sidebarExpended: true,
|
sidebarExpended: true,
|
||||||
dialogs: {},
|
dialogs: {},
|
||||||
@@ -20,6 +21,10 @@ export default createReducer(initialState, {
|
|||||||
state.pageSubtitle = action.pageSubtitle;
|
state.pageSubtitle = action.pageSubtitle;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[t.CHANGE_DASHBOARD_PAGE_HINT]: (state, action) => {
|
||||||
|
state.pageHint = action.pageHint;
|
||||||
|
},
|
||||||
|
|
||||||
[t.CHANGE_PREFERENCES_PAGE_TITLE]: (state, action) => {
|
[t.CHANGE_PREFERENCES_PAGE_TITLE]: (state, action) => {
|
||||||
state.preferencesPageTitle = action.pageTitle;
|
state.preferencesPageTitle = action.pageTitle;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ export default {
|
|||||||
CLOSE_DIALOG: 'CLOSE_DIALOG',
|
CLOSE_DIALOG: 'CLOSE_DIALOG',
|
||||||
CLOSE_ALL_DIALOGS: 'CLOSE_ALL_DIALOGS',
|
CLOSE_ALL_DIALOGS: 'CLOSE_ALL_DIALOGS',
|
||||||
CHANGE_DASHBOARD_PAGE_TITLE: 'CHANGE_DASHBOARD_PAGE_TITLE',
|
CHANGE_DASHBOARD_PAGE_TITLE: 'CHANGE_DASHBOARD_PAGE_TITLE',
|
||||||
|
CHANGE_DASHBOARD_PAGE_HINT: 'CHANGE_DASHBOARD_PAGE_HINT',
|
||||||
CHANGE_PREFERENCES_PAGE_TITLE: 'CHANGE_PREFERENCES_PAGE_TITLE',
|
CHANGE_PREFERENCES_PAGE_TITLE: 'CHANGE_PREFERENCES_PAGE_TITLE',
|
||||||
ALTER_DASHBOARD_PAGE_SUBTITLE: 'ALTER_DASHBOARD_PAGE_SUBTITLE',
|
ALTER_DASHBOARD_PAGE_SUBTITLE: 'ALTER_DASHBOARD_PAGE_SUBTITLE',
|
||||||
SET_TOPBAR_EDIT_VIEW: 'SET_TOPBAR_EDIT_VIEW',
|
SET_TOPBAR_EDIT_VIEW: 'SET_TOPBAR_EDIT_VIEW',
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { omit } from 'lodash';
|
import { omit, flatten } from 'lodash';
|
||||||
import ApiService from 'services/ApiService';
|
import ApiService from 'services/ApiService';
|
||||||
import t from 'store/types';
|
import t from 'store/types';
|
||||||
|
|
||||||
@@ -135,7 +135,14 @@ export const fetchManualJournalsTable = ({ query } = {}) => {
|
|||||||
});
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.MANUAL_JOURNALS_ITEMS_SET,
|
type: t.MANUAL_JOURNALS_ITEMS_SET,
|
||||||
manual_journals: response.data.manual_journals,
|
manual_journals: [
|
||||||
|
...response.data.manual_journals.map((manualJournal) => ({
|
||||||
|
...manualJournal,
|
||||||
|
entries: manualJournal.entries.map((entry) => ({
|
||||||
|
...omit(entry, ['account']),
|
||||||
|
}))
|
||||||
|
})),
|
||||||
|
]
|
||||||
});
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.MANUAL_JOURNALS_PAGINATION_SET,
|
type: t.MANUAL_JOURNALS_PAGINATION_SET,
|
||||||
@@ -145,6 +152,12 @@ export const fetchManualJournalsTable = ({ query } = {}) => {
|
|||||||
response.data.manual_journals?.viewMeta?.customViewId || -1,
|
response.data.manual_journals?.viewMeta?.customViewId || -1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
dispatch({
|
||||||
|
type: t.ACCOUNTS_ITEMS_SET,
|
||||||
|
accounts: flatten(response.data.manual_journals?.map(
|
||||||
|
journal => journal?.entries.map(entry => entry.account),
|
||||||
|
)),
|
||||||
|
});
|
||||||
dispatch({
|
dispatch({
|
||||||
type: t.MANUAL_JOURNALS_TABLE_LOADING,
|
type: t.MANUAL_JOURNALS_TABLE_LOADING,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|||||||
@@ -204,17 +204,20 @@ body.authentication {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// .page-form
|
||||||
|
// .page-form__header
|
||||||
|
// .page-form__content
|
||||||
|
// .page-form__floating-actions
|
||||||
.page-form{
|
.page-form{
|
||||||
|
|
||||||
&__header{
|
&__header{
|
||||||
padding: 20px;
|
background-color: #fbfbfb;
|
||||||
|
padding: 30px 20px 20px;
|
||||||
|
padding-bottom: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__primary-section{
|
&__primary-section{
|
||||||
background-color: #fbfbfb;
|
|
||||||
padding: 30px 20px 20px;
|
|
||||||
margin: -20px;
|
|
||||||
padding-bottom: 6px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__header-fields{
|
&__header-fields{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
.table .thead{
|
.table .thead{
|
||||||
.th{
|
.th{
|
||||||
border-bottom-color: #eaeaea;
|
border-bottom-color: #D2DDE2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25,11 +25,10 @@
|
|||||||
padding: 0.6rem 0.5rem;
|
padding: 0.6rem 0.5rem;
|
||||||
background: #fafafa;
|
background: #fafafa;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #445165;
|
color: #58667b;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
border-bottom: 1px solid rgb(224, 224, 224);
|
border-bottom: 1px solid rgb(224, 224, 224);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sort-icon{
|
.sort-icon{
|
||||||
width: 0;
|
width: 0;
|
||||||
height: 0;
|
height: 0;
|
||||||
@@ -145,7 +144,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.tr:hover .td{
|
.tr:hover .td{
|
||||||
background: #fafafa;
|
background: #f3f7fc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tr.is-context-menu-active .td{
|
.tr.is-context-menu-active .td{
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
|
||||||
.Pane.Pane2 {
|
.Pane.Pane2 {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Resizer {
|
.Resizer {
|
||||||
|
|||||||
@@ -19,13 +19,20 @@ $form-check-input-indeterminate-bg-image: url("data:image/svg+xml,<svg xmlns='ht
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Form
|
// Form
|
||||||
.bp3-label{
|
label.bp3-label{
|
||||||
color: #353535;
|
color: #353535;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
|
|
||||||
.required{
|
.required{
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.bp3-form-group.bp3-inline &{
|
||||||
|
margin: 0 10px 0 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding-top: calc(.3rem + 1px);
|
||||||
|
padding-bottom: calc(.3rem + 1px);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}-input{
|
.#{$ns}-input{
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
.dashboard__insider--bill-form{
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--bill{
|
.page-form--bill{
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|||||||
@@ -8,9 +8,11 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
#{$self}__primary-section{
|
#{$self}__primary-section{
|
||||||
padding: 30px 22px 0;
|
padding: 10px 0 0;
|
||||||
margin: -20px -20px 14px;
|
margin: 0 0 20px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
border-bottom: 1px solid #e4e4e4;
|
||||||
|
max-width: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bp3-form-group{
|
.bp3-form-group{
|
||||||
@@ -41,7 +43,6 @@
|
|||||||
&:not(:last-child) {
|
&:not(:last-child) {
|
||||||
padding-right: 10px;
|
padding-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.input-group--salutation-list{
|
&.input-group--salutation-list{
|
||||||
width: 25%;
|
width: 25%;
|
||||||
}
|
}
|
||||||
@@ -158,8 +159,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__floating-actions {
|
#{$self}__floating-actions {
|
||||||
margin-left: -20px;
|
margin-left: -40px;
|
||||||
margin-right: -20px;
|
margin-right: -40px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
|
|
||||||
&__topbar{
|
&__topbar{
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: 60px;
|
min-height: 60px;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
background-color: #fff;
|
||||||
border-bottom: 1px solid #F2EFEF;
|
border-bottom: 1px solid #F2EFEF;
|
||||||
|
|
||||||
&-right,
|
&-right,
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
|
|
||||||
&,
|
&,
|
||||||
&-group{
|
&-group{
|
||||||
height: 42px;
|
height: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.#{$ns}-navbar-divider{
|
.#{$ns}-navbar-divider{
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
}
|
}
|
||||||
&.bp3-minimal:active,
|
&.bp3-minimal:active,
|
||||||
&.bp3-minimal.bp3-active{
|
&.bp3-minimal.bp3-active{
|
||||||
background: rgba(167, 182, 194, 0.12);
|
background: #a7b6c21f;
|
||||||
color: #32304a;
|
color: #32304a;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.#{$ns}-icon{
|
.#{$ns}-icon{
|
||||||
color: #2A293D;
|
color: #32304a;
|
||||||
margin-right: 7px;
|
margin-right: 7px;
|
||||||
}
|
}
|
||||||
&.#{$ns}-minimal.#{$ns}-intent-danger{
|
&.#{$ns}-minimal.#{$ns}-intent-danger{
|
||||||
@@ -203,7 +203,7 @@
|
|||||||
|
|
||||||
h1{
|
h1{
|
||||||
font-size: 22px;
|
font-size: 22px;
|
||||||
color: #333363;
|
color: #48485c;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -233,13 +233,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__hint{
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
&__subtitle{
|
&__subtitle{
|
||||||
|
|
||||||
}
|
}
|
||||||
&__insider{
|
&__insider{
|
||||||
margin-bottom: 40px;
|
|
||||||
flex: 1 0 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__offline-badge{
|
&__offline-badge{
|
||||||
@@ -260,6 +264,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
min-width: 850px;
|
||||||
|
|
||||||
&:before{
|
&:before{
|
||||||
content: "";
|
content: "";
|
||||||
@@ -279,6 +284,8 @@
|
|||||||
&__insider{
|
&__insider{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
flex: 1 0 0;
|
||||||
|
background-color: #FBFBFB;
|
||||||
|
|
||||||
> .dashboard__loading-indicator{
|
> .dashboard__loading-indicator{
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
@@ -313,6 +320,23 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 0 0;
|
flex: 1 0 0;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
background: #fff;
|
||||||
|
margin: 20px;
|
||||||
|
border: 1px solid #d2dce2;
|
||||||
|
|
||||||
|
.bigcapital-datatable{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1 0 0;
|
||||||
|
|
||||||
|
.pagination{
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(.has-pagination){
|
||||||
|
padding-bottom: 30px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.datatable-empty-status{
|
.datatable-empty-status{
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
@@ -323,7 +347,6 @@
|
|||||||
|
|
||||||
&__preferences-topbar{
|
&__preferences-topbar{
|
||||||
border-bottom: 1px solid #E5E5E5;
|
border-bottom: 1px solid #E5E5E5;
|
||||||
// height: 70px;
|
|
||||||
height: 65px;
|
height: 65px;
|
||||||
padding: 0 0 0 22px;
|
padding: 0 0 0 22px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -362,6 +385,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__card{
|
||||||
|
border: 1px solid #d2dce2;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
&--page{
|
||||||
|
flex: 1 0 0;
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__error-boundary{
|
||||||
|
text-align: center;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: auto;
|
||||||
|
|
||||||
|
h1{
|
||||||
|
font-size: 26px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0px 0 10px;
|
||||||
|
color: #2c3a5d;
|
||||||
|
}
|
||||||
|
|
||||||
|
p{
|
||||||
|
font-size: 16px;
|
||||||
|
color: #1f3255;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bp3-icon{
|
||||||
|
margin-top: 6px;
|
||||||
|
|
||||||
|
path{
|
||||||
|
fill: #a1afca;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs--dashboard-views{
|
.tabs--dashboard-views{
|
||||||
@@ -408,7 +468,7 @@
|
|||||||
|
|
||||||
.navbar--dashboard-views{
|
.navbar--dashboard-views{
|
||||||
box-shadow: 0 0 0;
|
box-shadow: 0 0 0;
|
||||||
border-bottom: 1px solid #EAEAEA;
|
border-bottom: 1px solid #d2dce2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar-omnibar{
|
.navbar-omnibar{
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
.dashboard__insider--estimate-form{
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
.estimate-form {
|
.estimate-form {
|
||||||
padding-bottom: 30px;
|
padding-bottom: 30px;
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -185,195 +189,3 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// .estimate-form {
|
|
||||||
// padding-bottom: 30px;
|
|
||||||
// display: flex;
|
|
||||||
// flex-direction: column;
|
|
||||||
|
|
||||||
// .bp3-form-group {
|
|
||||||
// margin: 25px 20px 15px;
|
|
||||||
// width: 100%;
|
|
||||||
// .bp3-label {
|
|
||||||
// font-weight: 500;
|
|
||||||
// font-size: 13px;
|
|
||||||
// color: #444;
|
|
||||||
// width: 130px;
|
|
||||||
// }
|
|
||||||
// .bp3-form-content {
|
|
||||||
// // width: 400px;
|
|
||||||
// width: 45%;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // .expense-form-footer {
|
|
||||||
// // display: flex;
|
|
||||||
// // padding: 30px 25px 0;
|
|
||||||
// // justify-content: space-between;
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// &__primary-section {
|
|
||||||
// background: #fbfbfb;
|
|
||||||
// }
|
|
||||||
// &__table {
|
|
||||||
// padding: 15px 15px 0;
|
|
||||||
|
|
||||||
// .bp3-form-group {
|
|
||||||
// margin-bottom: 0;
|
|
||||||
// }
|
|
||||||
// .table {
|
|
||||||
// border: 1px dotted rgb(195, 195, 195);
|
|
||||||
// border-bottom: transparent;
|
|
||||||
// border-left: transparent;
|
|
||||||
|
|
||||||
// .th,
|
|
||||||
// .td {
|
|
||||||
// border-left: 1px dotted rgb(195, 195, 195);
|
|
||||||
|
|
||||||
// &.index {
|
|
||||||
// > span,
|
|
||||||
// > div {
|
|
||||||
// text-align: center;
|
|
||||||
// width: 100%;
|
|
||||||
// font-weight: 500;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .thead {
|
|
||||||
// .tr .th {
|
|
||||||
// padding: 10px 10px;
|
|
||||||
// background-color: #f2f5fa;
|
|
||||||
// font-size: 14px;
|
|
||||||
// font-weight: 500;
|
|
||||||
// color: #333;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .tbody {
|
|
||||||
// .tr .td {
|
|
||||||
// padding: 7px;
|
|
||||||
// border-bottom: 1px dotted rgb(195, 195, 195);
|
|
||||||
// min-height: 46px;
|
|
||||||
|
|
||||||
// &.index {
|
|
||||||
// background-color: #f2f5fa;
|
|
||||||
// text-align: center;
|
|
||||||
|
|
||||||
// > span {
|
|
||||||
// margin-top: auto;
|
|
||||||
// margin-bottom: auto;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .tr {
|
|
||||||
// .bp3-form-group .bp3-input,
|
|
||||||
// .form-group--select-list .bp3-button {
|
|
||||||
// border-radius: 3px;
|
|
||||||
// padding-left: 8px;
|
|
||||||
// padding-right: 8px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .bp3-form-group:not(.bp3-intent-danger) .bp3-input,
|
|
||||||
// .form-group--select-list:not(.bp3-intent-danger) .bp3-button {
|
|
||||||
// border-color: #e5e5e5;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// &:last-of-type {
|
|
||||||
// .td {
|
|
||||||
// border-bottom: transparent;
|
|
||||||
|
|
||||||
// .bp3-button,
|
|
||||||
// .bp3-input-group {
|
|
||||||
// display: none;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .td.actions {
|
|
||||||
// .bp3-button {
|
|
||||||
// background-color: transparent;
|
|
||||||
// color: #e68f8e;
|
|
||||||
|
|
||||||
// &:hover {
|
|
||||||
// color: #c23030;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// &.row--total {
|
|
||||||
// .td.amount {
|
|
||||||
// font-weight: bold;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .th {
|
|
||||||
// color: #444;
|
|
||||||
// font-weight: 600;
|
|
||||||
// border-bottom: 1px dotted #666;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .td {
|
|
||||||
// border-bottom: 1px dotted #999;
|
|
||||||
|
|
||||||
// &.description {
|
|
||||||
// .bp3-form-group {
|
|
||||||
// width: 100%;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .actions.td {
|
|
||||||
// .bp3-button {
|
|
||||||
// background: transparent;
|
|
||||||
// margin: 0;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// &__floating-footer {
|
|
||||||
// position: fixed;
|
|
||||||
// bottom: 0;
|
|
||||||
// width: 100%;
|
|
||||||
// background: #fff;
|
|
||||||
// padding: 18px 18px;
|
|
||||||
// border-top: 1px solid #ececec;
|
|
||||||
|
|
||||||
// .has-mini-sidebar & {
|
|
||||||
// left: 50px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .bp3-button {
|
|
||||||
// &.button--clear-lines {
|
|
||||||
// background-color: #fcefef;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .button--clear-lines,
|
|
||||||
// .button--new-line {
|
|
||||||
// padding-left: 14px;
|
|
||||||
// padding-right: 14px;
|
|
||||||
// }
|
|
||||||
// .dropzone-container {
|
|
||||||
// margin-top: 0;
|
|
||||||
// align-self: flex-end;
|
|
||||||
// }
|
|
||||||
// .dropzone {
|
|
||||||
// width: 300px;
|
|
||||||
// height: 75px;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// .form-group--description {
|
|
||||||
// .bp3-label {
|
|
||||||
// font-weight: 500;
|
|
||||||
// font-size: 13px;
|
|
||||||
// color: #444;
|
|
||||||
// }
|
|
||||||
// .bp3-form-content {
|
|
||||||
// // width: 280px;
|
|
||||||
// textarea {
|
|
||||||
// width: 450px;
|
|
||||||
// min-height: 75px;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
.dashboard__insider--estimate-form{
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--estimate{
|
.page-form--estimate{
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
padding-bottom: 80px;
|
padding-bottom: 80px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
&__header {
|
&__header {
|
||||||
padding: 25px 27px 20px;
|
padding: 25px 27px 20px;
|
||||||
|
|||||||
@@ -285,15 +285,18 @@
|
|||||||
&__list{
|
&__list{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: wrap;
|
flex-flow: wrap;
|
||||||
margin-left: -28px;
|
margin-left: -20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__item{
|
&__item{
|
||||||
width: 270px;
|
width: 270px;
|
||||||
margin-bottom: 40px;
|
margin-bottom: 20px;
|
||||||
margin-left: 28px;
|
margin-left: 20px;
|
||||||
border-top: 2px solid #DDD;
|
border: 1px solid #d1dee2;
|
||||||
|
border-top: 3px solid #d1dee2;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
|
background: #fff;
|
||||||
|
padding: 20px;
|
||||||
|
|
||||||
.title{
|
.title{
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -303,6 +306,7 @@
|
|||||||
color: rgb(31, 50, 85);
|
color: rgb(31, 50, 85);
|
||||||
line-height: 1.55;
|
line-height: 1.55;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
|
.dashboard__insider--invoice-form{
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--invoice{
|
.page-form--invoice{
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
|
|
||||||
|
|
||||||
.page-form--item{
|
.page-form--item{
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
@@ -8,9 +7,12 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
#{$self}__primary-section{
|
#{$self}__primary-section{
|
||||||
padding: 30px 22px 0;
|
|
||||||
margin: -20px -20px 24px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-bottom: 1px solid #eaeaea;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
max-width: 1000px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__body{
|
#{$self}__body{
|
||||||
@@ -65,8 +67,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
#{$self}__floating-actions{
|
#{$self}__floating-actions{
|
||||||
margin-left: -20px;
|
margin-left: -40px;
|
||||||
margin-right: -20px;
|
margin-right: -40px;
|
||||||
|
|
||||||
.form-group--active{
|
.form-group--active{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
|||||||
@@ -59,3 +59,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dashboard__insider{
|
||||||
|
|
||||||
|
&--make-journal-page{
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,9 +9,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tbody{
|
.tbody{
|
||||||
.amount > span{
|
.td.amount{
|
||||||
|
.bp3-popover-target{
|
||||||
|
border-bottom: 1px solid #e7e7e7;
|
||||||
|
}
|
||||||
|
> span{
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
.note{
|
.note{
|
||||||
.bp3-icon{
|
.bp3-icon{
|
||||||
color: #666;
|
color: #666;
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
.dashboard__insider--payment-made{
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--payment-made {
|
.page-form--payment-made {
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
|
||||||
|
.dashboard__insider--payment-receive-form{
|
||||||
|
background-color: #FFF;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--payment-receive {
|
.page-form--payment-receive {
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
.dashboard__insider--receipt-form{
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.page-form--receipt{
|
.page-form--receipt{
|
||||||
$self: '.page-form';
|
$self: '.page-form';
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ $sidebar-submenu-item-bg-color: #01287d;
|
|||||||
padding-top: 6px;
|
padding-top: 6px;
|
||||||
}
|
}
|
||||||
.#{$ns}-menu-item {
|
.#{$ns}-menu-item {
|
||||||
padding: 7px 16px;
|
padding: 8px 16px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
color: $sidebar-submenu-item-color;
|
color: $sidebar-submenu-item-color;
|
||||||
|
|
||||||
|
|||||||
@@ -198,7 +198,11 @@ export default class AccountsController extends BaseController{
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const account = await this.accountsService.editAccount(tenantId, accountId, accountDTO);
|
const account = await this.accountsService.editAccount(tenantId, accountId, accountDTO);
|
||||||
return res.status(200).send({ id: account.id });
|
|
||||||
|
return res.status(200).send({
|
||||||
|
id: account.id,
|
||||||
|
message: 'The account has been edited successfully',
|
||||||
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
next(error);
|
next(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,10 +107,12 @@ export default class AccountRepository extends TenantRepository {
|
|||||||
* @param {IAccount} account
|
* @param {IAccount} account
|
||||||
* @return {void}
|
* @return {void}
|
||||||
*/
|
*/
|
||||||
async edit(accountId: number, account: IAccount): Promise<void> {
|
async edit(accountId: number, accountInput: IAccount): Promise<void> {
|
||||||
const { Account } = this.models;
|
const { Account } = this.models;
|
||||||
await Account.query().findById(accountId).patch({ ...account });
|
const account = await Account.query().patchAndFetchById(accountId, { ...accountInput });
|
||||||
this.flushCache();
|
this.flushCache();
|
||||||
|
|
||||||
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -203,6 +203,8 @@ export default class AccountsService {
|
|||||||
* @param {IAccountDTO} accountDTO
|
* @param {IAccountDTO} accountDTO
|
||||||
*/
|
*/
|
||||||
public async editAccount(tenantId: number, accountId: number, accountDTO: IAccountDTO) {
|
public async editAccount(tenantId: number, accountId: number, accountDTO: IAccountDTO) {
|
||||||
|
this.logger.info('[account] trying to edit account.', { tenantId, accountId });
|
||||||
|
|
||||||
const { accountRepository } = this.tenancy.repositories(tenantId);
|
const { accountRepository } = this.tenancy.repositories(tenantId);
|
||||||
const oldAccount = await this.getAccountOrThrowError(tenantId, accountId);
|
const oldAccount = await this.getAccountOrThrowError(tenantId, accountId);
|
||||||
|
|
||||||
|
|||||||
@@ -407,6 +407,7 @@ export default class ManualJournalsService implements IManualJournalsService {
|
|||||||
this.logger.info('[manual_journals] trying to get manual journals list.', { tenantId, filter });
|
this.logger.info('[manual_journals] trying to get manual journals list.', { tenantId, filter });
|
||||||
const { results, pagination } = await ManualJournal.query().onBuild((builder) => {
|
const { results, pagination } = await ManualJournal.query().onBuild((builder) => {
|
||||||
dynamicList.buildQuery()(builder);
|
dynamicList.buildQuery()(builder);
|
||||||
|
builder.withGraphFetched('entries.account');
|
||||||
}).pagination(filter.page - 1, filter.pageSize);
|
}).pagination(filter.page - 1, filter.pageSize);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user