Compare commits

...

47 Commits

Author SHA1 Message Date
a.bouhuolia
77f0a767b3 Merge branch 'develop' 2021-09-30 16:41:57 +02:00
a.bouhuolia
8a982e5c7e feat: avoid changing setup plan slug. 2021-09-29 11:22:59 +02:00
a.bouhuolia
2f7564eb9c fix: tweaks in arabic localization. 2021-09-29 00:23:01 +02:00
a.bouhuolia
7c2c362585 Merge branch 'develop' of https://github.com/BigcapitalTech/bigcapital-client into develop 2021-09-29 00:11:34 +02:00
a.bouhuolia
786aad438a BIG-78: feat: prevent table row click action on some given nested elements. 2021-09-29 00:07:06 +02:00
a.bouhuolia
d6c78a9908 BIG-118: feat: hide the pagination before table has minimum items length. 2021-09-28 19:52:01 +02:00
a.bouhuolia
0aca6d9af7 BIG-125: prevent items entries from send amount computed attribute. 2021-09-28 18:37:10 +02:00
elforjani13
696943153d feat: quick payment receive & made in action Bar. 2021-09-28 18:32:30 +02:00
elforjani13
2e437d7b65 fix: style universal search. 2021-09-28 17:23:38 +02:00
elforjani13
5b12c4a433 fix: style universal input search. 2021-09-28 14:25:47 +02:00
a.bouhuolia
96269ccafb feat: remove blueprint config provider. 2021-09-28 13:53:43 +02:00
a.bouhuolia
f556f061cb chore: revert to @blueprint/core. 2021-09-28 13:46:30 +02:00
a.bouhuolia
b98e8aeeb4 chore: remove github token. 2021-09-28 12:27:23 +02:00
a.bouhuolia
17d5bbd9d1 chore: fix dockerfile. 2021-09-28 12:20:36 +02:00
a.bouhuolia
e1ab4e4d65 chore: fix dockerfile. 2021-09-28 12:14:32 +02:00
a.bouhuolia
b86a3a19dc chore: fix dockerfile. 2021-09-28 12:12:28 +02:00
a.bouhuolia
3b2796cb6d chore: fix dockerfile. 2021-09-28 11:59:04 +02:00
a.bouhuolia
15ee32f6a4 chore: fix docker build 2021-09-28 11:55:11 +02:00
a.bouhuolia
90e550c902 chore: add github registry login. 2021-09-28 11:52:05 +02:00
a.bouhuolia
cbc0ccbfb9 chore: add auth token to github registry. 2021-09-28 11:30:21 +02:00
a.bouhuolia
526c46b24d chore: copy npmrc file to docker container. 2021-09-28 11:06:27 +02:00
a.bouhuolia
6041c175fd chore: remove package-lock file. 2021-09-28 11:02:28 +02:00
a.bouhuolia
0f58665a0d chore: fix blueprintjs registry. 2021-09-28 10:58:41 +02:00
a.bouhuolia
91036c3e52 Merge branch 'develop' of https://github.com/BigcapitalTech/bigcapital-client into develop 2021-09-27 20:17:11 +02:00
a.bouhuolia
555e3a2434 feat: upgrade the blueprint core and datetitme. 2021-09-27 20:16:52 +02:00
elforjani13
b87e85d5ff BIG-124: Prevent the search Hotkey from writing to the field. 2021-09-27 12:45:32 +02:00
elforjani13
288225a0c1 feat: add quick payment receive & made in action Bar detail. 2021-09-26 22:08:13 +02:00
elforjani13
71f9fa47d4 BiG-5: Complete, add switch small and medium table row size. 2021-09-26 21:02:53 +02:00
elforjani13
fcace4213c feat: change default page size pagination. 2021-09-26 14:02:26 +02:00
a.bouhuolia
31d2b1b09a fix: switch small and medium table row size. 2021-09-26 13:14:03 +02:00
elforjani13
cd5116dbcb BIG-3: refactoring financial reports filtering. 2021-09-25 21:17:17 +02:00
elforjani13
010b660318 BIG-123: alert delete showing in vendor & customer. 2021-09-25 17:11:08 +02:00
elforjani13
4e99607b06 BIG-3: add filtering non-zero items. 2021-09-24 19:41:23 +02:00
elforjani13
a3f1857e91 Merge branch 'develop' of https://github.com/BigcapitalTech/bigcapital-client into develop 2021-09-23 15:42:14 +02:00
elforjani13
460ee2718e BIG-5: add switching between compact and medium row size. 2021-09-23 15:39:49 +02:00
a.bouhuolia
87938b8f41 Merge branch 'develop' of https://github.com/BigcapitalTech/bigcapital-client into develop 2021-09-23 13:03:18 +02:00
elforjani13
cd70bf1d80 BIG-94: handle estimate converted to invoice. 2021-09-23 13:02:50 +02:00
a.bouhuolia
c4f2ea405c BIG-119: fix formatted item type with universal search item. 2021-09-23 13:02:34 +02:00
a.bouhuolia
d1cb7eb51b Merge branch 'develop' of https://github.com/BigcapitalTech/bigcapital-client into develop 2021-09-23 12:13:24 +02:00
a.bouhuolia
e949b1b0c7 BIG-5: feat switching between compact and medium table row size. 2021-09-23 10:37:54 +02:00
a.bouhuolia
9b7382e222 BIG-18: missing locales in Arabic. 2021-09-23 09:53:59 +02:00
a.bouhuolia
364859a793 BIG-117: fix dashboard redirect all routes to homepage once refresh the page. 2021-09-23 09:44:30 +02:00
elforjani13
fd07306102 fix: inventory adjustment style. 2021-09-22 15:25:30 +02:00
elforjani13
5fc4897663 BIG-116: payment made transcation. 2021-09-22 13:56:43 +02:00
Ahmed Bouhuolia
7b85dfee5d chore: add circleci build and publish workflow. 2021-09-21 22:32:14 +02:00
Ahmed Bouhuolia
14012c4d7e test circleci 2021-09-21 22:25:24 +02:00
elforjani13
ecf56f3b99 fix: table header overflow. 2021-09-21 22:09:41 +02:00
179 changed files with 1959 additions and 18468 deletions

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
@bigcapitalhq:registry=https://npm.pkg.github.com

View File

@@ -6,6 +6,15 @@ WORKDIR /app
COPY ./package.json /app/package.json COPY ./package.json /app/package.json
COPY ./package-lock.json /app/package-lock.json COPY ./package-lock.json /app/package-lock.json
COPY ./.npmrc /app/.npmrc
ARG GITHUB_USERNAME
ARG GITHUB_PASS
ARG GITHUB_EMAIL
RUN npm install -g npm-cli-login
RUN npm-cli-login -s @bigcapitalhq -r https://npm.pkg.github.com -u $GITHUB_USERNAME -p $GITHUB_PASS -e $GITHUB_EMAIL
RUN npm install RUN npm install

17639
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -4,8 +4,8 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@babel/core": "7.8.4", "@babel/core": "7.8.4",
"@blueprintjs/core": "^3.38.1", "@blueprintjs/core": "^3.50.2",
"@blueprintjs/datetime": "^3.15.2", "@blueprintjs/datetime": "^3.23.12",
"@blueprintjs/popover2": "^0.11.1", "@blueprintjs/popover2": "^0.11.1",
"@blueprintjs/select": "^3.11.2", "@blueprintjs/select": "^3.11.2",
"@blueprintjs/table": "^3.8.3", "@blueprintjs/table": "^3.8.3",

View File

@@ -13,3 +13,9 @@ export const TABLES = {
MANUAL_JOURNALS: 'manual_journal', MANUAL_JOURNALS: 'manual_journal',
EXPENSES: 'expenses', EXPENSES: 'expenses',
}; };
export const TABLE_SIZE = {
COMPACT: 'compact',
SMALL: 'small',
MEDIUM: 'medium',
}

View File

@@ -4,8 +4,9 @@ import { DateInput } from '@blueprintjs/datetime';
import moment from 'moment'; import moment from 'moment';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { isUndefined } from 'lodash'; import { isUndefined } from 'lodash';
import { useAutofocus } from 'hooks'; import { useAutofocus } from 'hooks';
import { Choose, ListSelect } from 'components'; import { T, Choose, ListSelect } from 'components';
import { momentFormatter } from 'utils'; import { momentFormatter } from 'utils';
function AdvancedFilterEnumerationField({ options, value, ...rest }) { function AdvancedFilterEnumerationField({ options, value, ...rest }) {
@@ -19,7 +20,7 @@ function AdvancedFilterEnumerationField({ options, value, ...rest }) {
minimal: true, minimal: true,
captureDismiss: true, captureDismiss: true,
}} }}
defaultText={`Select an option`} defaultText={<T id={'filter.select_option'} />}
textProp={'label'} textProp={'label'}
selectedItemProp={'key'} selectedItemProp={'key'}
{...rest} {...rest}
@@ -32,8 +33,7 @@ const IFieldType = {
BOOLEAN: 'boolean', BOOLEAN: 'boolean',
NUMBER: 'number', NUMBER: 'number',
DATE: 'date', DATE: 'date',
} };
function tansformDateValue(date, defaultValue = null) { function tansformDateValue(date, defaultValue = null) {
return date ? moment(date).toDate() : defaultValue; return date ? moment(date).toDate() : defaultValue;
@@ -46,13 +46,13 @@ export default function AdvancedFilterValueField2({
fieldType, fieldType,
options, options,
onChange, onChange,
isFocus isFocus,
}) { }) {
const [localValue, setLocalValue] = React.useState(value); const [localValue, setLocalValue] = React.useState(value);
React.useEffect(() => { React.useEffect(() => {
if (localValue !== value && !isUndefined(value)) { if (localValue !== value && !isUndefined(value)) {
setLocalValue(value) setLocalValue(value);
} }
}, [localValue, value]); }, [localValue, value]);
@@ -106,10 +106,10 @@ export default function AdvancedFilterValueField2({
position: Position.BOTTOM, position: Position.BOTTOM,
}} }}
shortcuts={true} shortcuts={true}
placeholder={'Enter date'} placeholder={intl.get('filter.enter_date')}
fill={true} fill={true}
inputProps={{ inputProps={{
fill: true fill: true,
}} }}
/> />
</Choose.When> </Choose.When>
@@ -120,7 +120,7 @@ export default function AdvancedFilterValueField2({
<Choose.Otherwise> <Choose.Otherwise>
<InputGroup <InputGroup
placeholder={intl.get('value')} placeholder={intl.get('filter.value')}
onChange={handleInputChange} onChange={handleInputChange}
value={localValue} value={localValue}
inputRef={valueRef} inputRef={valueRef}
@@ -128,4 +128,4 @@ export default function AdvancedFilterValueField2({
</Choose.Otherwise> </Choose.Otherwise>
</Choose> </Choose>
); );
} }

View File

@@ -6,15 +6,36 @@ import { ReactQueryDevtools } from 'react-query/devtools';
import 'style/App.scss'; import 'style/App.scss';
import 'moment/locale/ar-ly'; import 'moment/locale/ar-ly';
import 'moment/locale/es-us' import 'moment/locale/es-us';
import AppIntlLoader from './AppIntlLoader'; import AppIntlLoader from './AppIntlLoader';
import PrivateRoute from 'components/Guards/PrivateRoute'; import PrivateRoute from 'components/Guards/PrivateRoute';
import GlobalErrors from 'containers/GlobalErrors/GlobalErrors'; import GlobalErrors from 'containers/GlobalErrors/GlobalErrors';
import DashboardPrivatePages from 'components/Dashboard/PrivatePages'; import DashboardPrivatePages from 'components/Dashboard/PrivatePages';
import Authentication from 'components/Authentication'; import Authentication from 'components/Authentication';
import { SplashScreen } from '../components'; import { SplashScreen } from '../components';
import { queryConfig } from '../hooks/query/base' import { queryConfig } from '../hooks/query/base';
/**
* App inner.
*/
function AppInsider({ history }) {
return (
<div className="App">
<Router history={history}>
<Switch>
<Route path={'/auth'} component={Authentication} />
<Route path={'/'}>
<PrivateRoute component={DashboardPrivatePages} />
</Route>
</Switch>
</Router>
<GlobalErrors />
</div>
);
}
/** /**
* Core application. * Core application.
@@ -31,21 +52,10 @@ export default function App() {
<SplashScreen /> <SplashScreen />
<AppIntlLoader> <AppIntlLoader>
<div className="App"> <AppInsider history={history} />
<Router history={history}>
<Switch>
<Route path={'/auth'} component={Authentication} />
<Route path={'/'}>
<PrivateRoute component={DashboardPrivatePages} />
</Route>
</Switch>
</Router>
<GlobalErrors />
</div>
</AppIntlLoader> </AppIntlLoader>
<ReactQueryDevtools initialIsOpen /> <ReactQueryDevtools initialIsOpen />
</QueryClientProvider> </QueryClientProvider>
); );
} }

View File

@@ -7,9 +7,10 @@ import rtlDetect from 'rtl-detect';
import * as R from 'ramda'; import * as R from 'ramda';
import { AppIntlProvider } from './AppIntlProvider'; import { AppIntlProvider } from './AppIntlProvider';
import { useSplashLoading } from '../hooks/state';
import { useWatch } from '../hooks';
import withDashboardActions from '../containers/Dashboard/withDashboardActions'; import withDashboardActions from '../containers/Dashboard/withDashboardActions';
import withDashboard from '../containers/Dashboard/withDashboard';
const SUPPORTED_LOCALES = [ const SUPPORTED_LOCALES = [
{ name: 'English', value: 'en' }, { name: 'English', value: 'en' },
@@ -63,20 +64,13 @@ function transformMomentLocale(currentLocale) {
} }
/** /**
* Application Intl loader. * Loads application locales of the given current locale.
* @param {string} currentLocale
* @returns {{ isLoading: boolean }}
*/ */
function AppIntlLoader({ appIntlIsLoading, setAppIntlIsLoading, children }) { function useAppLoadLocales(currentLocale) {
const [isLocalsLoading, setIsLocalsLoading] = React.useState(true); const [startLoading, stopLoading] = useSplashLoading();
const [isYupLoading, setIsYupLoading] = React.useState(true); const [isLoading, setIsLoading] = React.useState(true);
// Retrieve the current locale.
const currentLocale = getCurrentLocal();
// Detarmines the document direction based on the given locale.
const isRTL = rtlDetect.isRtlLang(currentLocale);
// Modifies the html document direction
useDocumentDirectionModifier(currentLocale, isRTL);
React.useEffect(() => { React.useEffect(() => {
// Lodas the locales data file. // Lodas the locales data file.
@@ -91,33 +85,72 @@ function AppIntlLoader({ appIntlIsLoading, setAppIntlIsLoading, children }) {
}) })
.then(() => { .then(() => {
moment.locale(transformMomentLocale(currentLocale)); moment.locale(transformMomentLocale(currentLocale));
setIsLocalsLoading(false); setIsLoading(false);
}); });
}, [currentLocale, setIsLocalsLoading]); }, [currentLocale, stopLoading]);
// Watches the value to start/stop splash screen.
useWatch(isLoading, (value) => (value ? startLoading() : stopLoading()), {
immediate: true,
});
return { isLoading };
}
/**
* Loads application yup locales based on the given current locale.
* @param {string} currentLocale
* @returns {{ isLoading: boolean }}
*/
function useAppYupLoadLocales(currentLocale) {
const [startLoading, stopLoading] = useSplashLoading();
const [isLoading, setIsLoading] = React.useState(true);
React.useEffect(() => { React.useEffect(() => {
loadYupLocales(currentLocale) loadYupLocales(currentLocale)
.then(({ locale }) => { .then(({ locale }) => {
setLocale(locale); setLocale(locale);
setIsYupLoading(false); setIsLoading(false);
}) })
.then(() => {}); .then(() => {});
}, [currentLocale]); }, [currentLocale, stopLoading]);
React.useEffect(() => { // Watches the valiue to start/stop splash screen.
if (!isLocalsLoading && !isYupLoading) { useWatch(isLoading, (value) => (value ? startLoading() : stopLoading()), {
setAppIntlIsLoading(false); immediate: true,
}
}); });
return { isLoading };
}
/**
* Application Intl loader.
*/
function AppIntlLoader({ children }) {
// Retrieve the current locale.
const currentLocale = getCurrentLocal();
// Detarmines the document direction based on the given locale.
const isRTL = rtlDetect.isRtlLang(currentLocale);
// Modifies the html document direction
useDocumentDirectionModifier(currentLocale, isRTL);
// Loads yup localization of the given locale.
const { isLoading: isAppYupLocalesLoading } =
useAppYupLoadLocales(currentLocale);
// Loads application locales of the given locale.
const { isLoading: isAppLocalesLoading } = useAppLoadLocales(currentLocale);
// Detarmines whether the app locales loading.
const isLoading = isAppYupLocalesLoading && isAppLocalesLoading;
return ( return (
<AppIntlProvider currentLocale={currentLocale} isRTL={isRTL}> <AppIntlProvider currentLocale={currentLocale} isRTL={isRTL}>
{appIntlIsLoading ? null : children} {isLoading ? null : children}
</AppIntlProvider> </AppIntlProvider>
); );
} }
export default R.compose( export default R.compose(withDashboardActions)(AppIntlLoader);
withDashboardActions,
withDashboard(({ appIntlIsLoading }) => ({ appIntlIsLoading })),
)(AppIntlLoader);

View File

@@ -10,6 +10,7 @@ function AppIntlProvider({ currentLocale, isRTL, children }) {
currentLocale, currentLocale,
isRTL, isRTL,
isLTR: !isRTL, isLTR: !isRTL,
direction: isRTL ? 'rtl' : 'ltr',
}; };
return ( return (

View File

@@ -6,7 +6,6 @@ import authenticationRoutes from 'routes/authentication';
import { FormattedMessage as T } from 'components'; import { FormattedMessage as T } from 'components';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import { useIsAuthenticated } from 'hooks/state'; import { useIsAuthenticated } from 'hooks/state';
import {AuthenticationBoot} from '../containers/Authentication/AuthenticationBoot';
import 'style/pages/Authentication/Auth.scss'; import 'style/pages/Authentication/Auth.scss';
function PageFade(props) { function PageFade(props) {
@@ -26,7 +25,6 @@ export default function AuthenticationWrapper({ ...rest }) {
) : ( ) : (
<BodyClassName className={'authentication'}> <BodyClassName className={'authentication'}>
<div class="authentication-page"> <div class="authentication-page">
<AuthenticationBoot />
<a <a
href={'http://bigcapital.ly'} href={'http://bigcapital.ly'}
className={'authentication-page__goto-bigcapital'} className={'authentication-page__goto-bigcapital'}

View File

@@ -0,0 +1,10 @@
import React from 'react';
import { firstLettersArgs } from 'utils';
export default function AvatarCell({ row: { original }, size }) {
return (
<span className="avatar" data-size={size}>
{firstLettersArgs(original?.display_name)}
</span>
);
}

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
export default function Card({ className, children }) { export default function Card({ className, children }) {
return <div className={classNames('card', className)}>{children}</div>; return <div className={classNames('card', className)}>{children}</div>;
} }

View File

@@ -1,23 +1,30 @@
import React from 'react'; import React from 'react';
import * as R from 'ramda'; import * as R from 'ramda';
import { useUser, useCurrentOrganization } from 'hooks/query'; import { useUser, useCurrentOrganization } from '../../hooks/query';
import { useSplashLoading } from '../../hooks/state';
import { useWatch, useWhen } from '../../hooks';
import withAuthentication from '../../containers/Authentication/withAuthentication'; import withAuthentication from '../../containers/Authentication/withAuthentication';
import withDashboardActions from '../../containers/Dashboard/withDashboardActions';
import { setCookie, getCookie } from '../../utils'; import { setCookie, getCookie } from '../../utils';
/** /**
* Dashboard async booting. * Dashboard async booting.
*/ */
function DashboardBootJSX({ setAppIsLoading, authenticatedUserId }) { function DashboardBootJSX({ authenticatedUserId }) {
// Fetches the current user's organization. // Fetches the current user's organization.
const { isSuccess: isCurrentOrganizationSuccess, data: organization } = const {
useCurrentOrganization(); isSuccess: isCurrentOrganizationSuccess,
isLoading: isOrgLoading,
data: organization,
} = useCurrentOrganization();
// Authenticated user. // Authenticated user.
const { isSuccess: isAuthUserSuccess, data: authUser } = const {
useUser(authenticatedUserId); isSuccess: isAuthUserSuccess,
isLoading: isAuthUserLoading,
} = useUser(authenticatedUserId);
// Initial locale cookie value. // Initial locale cookie value.
const localeCookie = getCookie('locale'); const localeCookie = getCookie('locale');
@@ -48,23 +55,39 @@ function DashboardBootJSX({ setAppIsLoading, authenticatedUserId }) {
} }
}, [localeCookie, organization]); }, [localeCookie, organization]);
React.useEffect(() => { const [startLoading, stopLoading] = useSplashLoading();
// Once the all requests complete change the app loading state.
if ( // Splash loading when organization request loading and
isAuthUserSuccess && // applicaiton still not booted.
useWatch(isOrgLoading, (value) => {
value && !isBooted.current && startLoading();
});
// Splash loading when request authenticated user loading and
// application still not booted yet.
useWatch(isAuthUserLoading, (value) => {
value && !isBooted.current && startLoading();
});
// Stop splash loading once organization request success.
useWatch(isCurrentOrganizationSuccess, (value) => {
value && stopLoading();
});
// Stop splash loading once authenticated user request success.
useWatch(isAuthUserSuccess, (value) => {
value && stopLoading();
});
// Once the all requests complete change the app loading state.
useWhen(
isAuthUserSuccess &&
isCurrentOrganizationSuccess && isCurrentOrganizationSuccess &&
localeCookie === organization?.metadata?.language localeCookie === organization?.metadata?.language,
) { () => {
setAppIsLoading(false);
isBooted.current = true; isBooted.current = true;
} },
}, [ );
isAuthUserSuccess,
isCurrentOrganizationSuccess,
organization,
setAppIsLoading,
localeCookie,
]);
return null; return null;
} }
@@ -72,5 +95,4 @@ export const DashboardBoot = R.compose(
withAuthentication(({ authenticatedUserId }) => ({ withAuthentication(({ authenticatedUserId }) => ({
authenticatedUserId, authenticatedUserId,
})), })),
withDashboardActions,
)(DashboardBootJSX); )(DashboardBootJSX);

View File

@@ -1,36 +0,0 @@
import React from 'react';
import {
Button,
PopoverInteractionKind,
Popover,
Menu,
MenuItem,
MenuDivider,
Classes
} from '@blueprintjs/core';
import { Icon } from 'components';
export function DashboardRowsHeightButton() {
return (
<Popover
minimal={true}
content={
<Menu>
<MenuDivider title={'Rows height'} />
<MenuItem text="Compact" />
<MenuItem text="Medium" />
</Menu>
}
placement="bottom-start"
modifiers={{
offset: { offset: '0, 4' },
}}
interactionKind={PopoverInteractionKind.CLICK}
>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="rows-height" iconSize={16} />}
/>
</Popover>
);
}

View File

@@ -0,0 +1,70 @@
import React from 'react';
import {
Button,
PopoverInteractionKind,
Popover,
Menu,
MenuItem,
MenuDivider,
Classes,
Tooltip,
Position,
} from '@blueprintjs/core';
import clsx from 'classnames';
import { Icon, T } from 'components';
import Style from './style.module.scss';
/**
* Dashboard rows height button control.
*/
export function DashboardRowsHeightButton({ initialValue, value, onChange }) {
const [localSize, setLocalSize] = React.useState(initialValue);
// Handle menu item click.
const handleItemClick = (size) => (event) => {
setLocalSize(size);
onChange && onChange(size, event);
};
// Button icon name.
const btnIcon = `table-row-${localSize}`;
return (
<Popover
minimal={true}
content={
<Menu className={Style.menu}>
<MenuDivider title={<T id={'dashboard.rows_height'} />} />
<MenuItem
onClick={handleItemClick('small')}
text={<T id={'dashboard.row_small'} />}
/>
<MenuItem
onClick={handleItemClick('medium')}
text={<T id={'dashboard.row_medium'} />}
/>
</Menu>
}
placement="bottom-start"
modifiers={{
offset: { offset: '0, 4' },
}}
interactionKind={PopoverInteractionKind.CLICK}
>
<Tooltip
content={<T id={'dashboard.rows_height'} />}
minimal={true}
position={Position.BOTTOM}
>
<Button
className={clsx(Classes.MINIMAL, Style.button)}
icon={<Icon icon={btnIcon} iconSize={16} />}
/>
</Tooltip>
</Popover>
);
}
DashboardRowsHeightButton.defaultProps = {
initialValue: 'medium',
};

View File

@@ -0,0 +1,12 @@
.menu{
:global .bp3-heading{
font-weight: 400;
opacity: 0.5;
font-size: 12px;
}
}
.button{
min-width: 34px;
}

View File

@@ -4,10 +4,9 @@ import { Switch, Route } from 'react-router';
import Dashboard from 'components/Dashboard/Dashboard'; import Dashboard from 'components/Dashboard/Dashboard';
import SetupWizardPage from 'containers/Setup/WizardSetupPage'; import SetupWizardPage from 'containers/Setup/WizardSetupPage';
import EnsureOrganizationIsReady from 'components/Guards/EnsureOrganizationIsReady'; import EnsureOrganizationIsReady from '../../components/Guards/EnsureOrganizationIsReady';
import EnsureOrganizationIsNotReady from 'components/Guards/EnsureOrganizationIsNotReady'; import EnsureOrganizationIsNotReady from '../../components/Guards/EnsureOrganizationIsNotReady';
import { PrivatePagesProvider } from './PrivatePagesProvider'; import { PrivatePagesProvider } from './PrivatePagesProvider';
import { DashboardBoot } from '../../components';
import 'style/pages/Dashboard/Dashboard.scss'; import 'style/pages/Dashboard/Dashboard.scss';
@@ -17,8 +16,6 @@ import 'style/pages/Dashboard/Dashboard.scss';
export default function DashboardPrivatePages() { export default function DashboardPrivatePages() {
return ( return (
<PrivatePagesProvider> <PrivatePagesProvider>
<DashboardBoot />
<Switch> <Switch>
<Route path={'/setup'}> <Route path={'/setup'}>
<EnsureOrganizationIsNotReady> <EnsureOrganizationIsNotReady>

View File

@@ -1,9 +1,31 @@
import React from 'react'; import React from 'react';
import * as R from 'ramda';
import { AuthenticatedUser } from './AuthenticatedUser'; import { AuthenticatedUser } from './AuthenticatedUser';
import { DashboardBoot } from '../../components';
import withDashboard from '../../containers/Dashboard/withDashboard';
/** /**
* Private pages provider. * Private pages provider.
*/ */
export function PrivatePagesProvider({ children }) { function PrivatePagesProviderComponent({
return <AuthenticatedUser>{children}</AuthenticatedUser>; splashScreenCompleted,
// #ownProps
children,
}) {
return (
<AuthenticatedUser>
<DashboardBoot />
{splashScreenCompleted ? children : null}
</AuthenticatedUser>
);
} }
export const PrivatePagesProvider = R.compose(
withDashboard(({ splashScreenCompleted }) => ({
splashScreenCompleted,
})),
)(PrivatePagesProviderComponent);

View File

@@ -3,13 +3,12 @@ import * as R from 'ramda';
import BigcapitalLoading from './BigcapitalLoading'; import BigcapitalLoading from './BigcapitalLoading';
import withDashboard from '../../containers/Dashboard/withDashboard'; import withDashboard from '../../containers/Dashboard/withDashboard';
function SplashScreenComponent({ appIsLoading, appIntlIsLoading }) { function SplashScreenComponent({ splashScreenLoading }) {
return appIsLoading || appIntlIsLoading ? <BigcapitalLoading /> : null; return splashScreenLoading ? <BigcapitalLoading /> : null;
} }
export const SplashScreen = R.compose( export const SplashScreen = R.compose(
withDashboard(({ appIsLoading, appIntlIsLoading }) => ({ withDashboard(({ splashScreenLoading }) => ({
appIsLoading, splashScreenLoading,
appIntlIsLoading,
})), })),
)(SplashScreenComponent); )(SplashScreenComponent);

View File

@@ -196,6 +196,9 @@ export default function DataTable(props) {
DataTable.defaultProps = { DataTable.defaultProps = {
pagination: false, pagination: false,
hidePaginationNoPages: true,
size: null,
spinnerProps: { size: 30 }, spinnerProps: { size: 30 },
expandToggleColumn: 1, expandToggleColumn: 1,

View File

@@ -4,9 +4,11 @@ import { If } from 'components';
import { Skeleton } from 'components'; import { Skeleton } from 'components';
import { useAppIntlContext } from 'components/AppIntlProvider'; import { useAppIntlContext } from 'components/AppIntlProvider';
import TableContext from './TableContext'; import TableContext from './TableContext';
import { saveInvoke } from 'utils'; import { saveInvoke, ignoreEventFromSelectors } from 'utils';
import { isCellLoading } from './utils'; import { isCellLoading } from './utils';
const ROW_CLICK_SELECTORS_INGORED = ['.expand-toggle', '.selection-checkbox'];
/** /**
* Table cell. * Table cell.
*/ */
@@ -50,6 +52,9 @@ export default function TableCell({ cell, row, index }) {
} }
// Handle cell click action. // Handle cell click action.
const handleCellClick = (event) => { const handleCellClick = (event) => {
if (ignoreEventFromSelectors(event, ROW_CLICK_SELECTORS_INGORED)) {
return;
}
saveInvoke(onCellClick, cell, event); saveInvoke(onCellClick, cell, event);
}; };
@@ -58,7 +63,7 @@ export default function TableCell({ cell, row, index }) {
{...cell.getCellProps({ {...cell.getCellProps({
className: classNames(cell.column.className, 'td', { className: classNames(cell.column.className, 'td', {
'is-text-overview': cell.column.textOverview, 'is-text-overview': cell.column.textOverview,
'clickable': cell.column.clickable, clickable: cell.column.clickable,
'align-right': cell.column.align === 'right', 'align-right': cell.column.align === 'right',
}), }),
onClick: handleCellClick, onClick: handleCellClick,
@@ -83,11 +88,15 @@ export default function TableCell({ cell, row, index }) {
// to build the toggle for expanding a row // to build the toggle for expanding a row
} }
<If condition={cell.row.canExpand && expandable && isExpandColumn}> <If condition={cell.row.canExpand && expandable && isExpandColumn}>
<span {...getToggleRowExpandedProps({ className: 'expand-toggle' })}> <span
{...getToggleRowExpandedProps({
className: 'expand-toggle',
})}
style={{}}
>
<span <span
className={classNames({ className={classNames('expand-arrow', {
'arrow-down': isExpanded, 'is-expanded': isExpanded,
'arrow-right': !isExpanded,
})} })}
/> />
</span> </span>

View File

@@ -3,7 +3,7 @@ import { Checkbox } from '@blueprintjs/core';
export default function TableIndeterminateCheckboxRow({ row }) { export default function TableIndeterminateCheckboxRow({ row }) {
return ( return (
<div> <div class="selection-checkbox">
<Checkbox {...row.getToggleRowSelectedProps()} /> <Checkbox {...row.getToggleRowSelectedProps()} />
</div> </div>
); );

View File

@@ -14,12 +14,15 @@ export default function TablePagination() {
pageCount, pageCount,
state: { pageIndex, pageSize }, state: { pageIndex, pageSize },
}, },
props: { pagination, loading, onPaginationChange }, props: { pagination, loading, onPaginationChange, hidePaginationNoPages },
} = useContext(TableContext); } = useContext(TableContext);
const triggerOnPaginationChange = useCallback((payload) => { const triggerOnPaginationChange = useCallback(
saveInvoke(onPaginationChange, payload) (payload) => {
}, [onPaginationChange]); saveInvoke(onPaginationChange, payload);
},
[onPaginationChange],
);
// Handles the page changing. // Handles the page changing.
const handlePageChange = useCallback( const handlePageChange = useCallback(
@@ -45,8 +48,14 @@ export default function TablePagination() {
[gotoPage, setPageSize, triggerOnPaginationChange], [gotoPage, setPageSize, triggerOnPaginationChange],
); );
// Detarmines when display the pagination.
const showPagination =
pagination &&
((hidePaginationNoPages && pageCount > 1) || !hidePaginationNoPages) &&
!loading;
return ( return (
<If condition={pagination && !loading}> showPagination && (
<Pagination <Pagination
currentPage={pageIndex + 1} currentPage={pageIndex + 1}
total={pageSize * pageCount} total={pageSize * pageCount}
@@ -54,6 +63,6 @@ export default function TablePagination() {
onPageChange={handlePageChange} onPageChange={handlePageChange}
onPageSizeChange={handlePageSizeChange} onPageSizeChange={handlePageSizeChange}
/> />
</If> )
); );
} }

View File

@@ -1,5 +1,5 @@
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import classNames from 'classnames'; import clsx from 'classnames';
import { ScrollSync } from 'react-scroll-sync'; import { ScrollSync } from 'react-scroll-sync';
import TableContext from './TableContext'; import TableContext from './TableContext';
@@ -9,12 +9,20 @@ import TableContext from './TableContext';
export default function TableWrapper({ children }) { export default function TableWrapper({ children }) {
const { const {
table: { getTableProps }, table: { getTableProps },
props: { sticky, pagination, loading, expandable, virtualizedRows, className }, props: {
sticky,
pagination,
loading,
expandable,
virtualizedRows,
className,
size,
},
} = useContext(TableContext); } = useContext(TableContext);
return ( return (
<div <div
className={classNames('bigcapital-datatable', className, { className={clsx('bigcapital-datatable', className, {
'has-sticky': sticky, 'has-sticky': sticky,
'has-pagination': pagination, 'has-pagination': pagination,
'is-expandable': expandable, 'is-expandable': expandable,
@@ -25,7 +33,9 @@ export default function TableWrapper({ children }) {
<ScrollSync> <ScrollSync>
<div <div
{...getTableProps({ style: { minWidth: 'none' } })} {...getTableProps({ style: { minWidth: 'none' } })}
className="table" className={clsx('table', {
[`table-size--${size}`]: size,
})}
> >
{children} {children}
</div> </div>

View File

@@ -6,7 +6,6 @@ import { compose } from 'utils';
import withAuthentication from 'containers/Authentication/withAuthentication'; import withAuthentication from 'containers/Authentication/withAuthentication';
import withOrganization from 'containers/Organization/withOrganization'; import withOrganization from 'containers/Organization/withOrganization';
function EnsureOrganizationIsReady({ function EnsureOrganizationIsReady({
// #ownProps // #ownProps
children, children,
@@ -15,10 +14,10 @@ function EnsureOrganizationIsReady({
// #withOrganizationByOrgId // #withOrganizationByOrgId
isOrganizationReady, isOrganizationReady,
}) { }) {
return (isOrganizationReady) ? children : ( return isOrganizationReady ? (
<Redirect children
to={{ pathname: redirectTo }} ) : (
/> <Redirect to={{ pathname: redirectTo }} />
); );
} }
@@ -28,4 +27,4 @@ export default compose(
organizationId: props.currentOrganizationId, organizationId: props.currentOrganizationId,
})), })),
withOrganization(({ isOrganizationReady }) => ({ isOrganizationReady })), withOrganization(({ isOrganizationReady }) => ({ isOrganizationReady })),
)(EnsureOrganizationIsReady); )(EnsureOrganizationIsReady);

View File

@@ -86,7 +86,7 @@ function Pagination({
currentPage, currentPage,
total, total,
size, size,
pageSizesOptions = [5, 12, 20, 30, 50, 75, 100, 150], pageSizesOptions = [20, 30, 50, 75, 100, 150],
onPageChange, onPageChange,
onPageSizeChange, onPageSizeChange,
}) { }) {

View File

@@ -141,7 +141,6 @@ function UniversalSearchBar({ isOpen, onSearchTypeChange, ...listProps }) {
{...handlers} {...handlers}
> >
<InputGroup <InputGroup
autoFocus={true}
large={true} large={true}
leftIcon={<Icon icon={'universal-search'} iconSize={20} />} leftIcon={<Icon icon={'universal-search'} iconSize={20} />}
placeholder={intl.get('universal_search.placeholder')} placeholder={intl.get('universal_search.placeholder')}

View File

@@ -58,6 +58,7 @@ import AccountsSuggestField from './AccountsSuggestField';
import MaterialProgressBar from './MaterialProgressBar'; import MaterialProgressBar from './MaterialProgressBar';
import { MoneyFieldCell } from './DataTableCells'; import { MoneyFieldCell } from './DataTableCells';
import Card from './Card'; import Card from './Card';
import AvaterCell from './AvaterCell';
import { ItemsMultiSelect } from './Items'; import { ItemsMultiSelect } from './Items';
@@ -71,13 +72,13 @@ export * from './PdfPreview';
export * from './Details'; export * from './Details';
export * from './Drawer/DrawerInsider'; export * from './Drawer/DrawerInsider';
export * from './Drawer/DrawerMainTabs'; export * from './Drawer/DrawerMainTabs';
export * from './TotalLines/index' export * from './TotalLines/index';
export * from './Alert'; export * from './Alert';
export * from './Subscriptions'; export * from './Subscriptions';
export * from './Dashboard'; export * from './Dashboard';
export * from './Drawer'; export * from './Drawer';
export * from './Forms'; export * from './Forms';
export * from './MultiSelectTaggable' export * from './MultiSelectTaggable';
export * from './Utils/FormatNumber'; export * from './Utils/FormatNumber';
export * from './Utils/FormatDate'; export * from './Utils/FormatDate';
@@ -150,4 +151,5 @@ export {
MoneyFieldCell, MoneyFieldCell,
ItemsMultiSelect, ItemsMultiSelect,
Card, Card,
AvaterCell,
}; };

View File

@@ -12,6 +12,7 @@ import { useHistory } from 'react-router-dom';
import { import {
AdvancedFilterPopover, AdvancedFilterPopover,
DashboardFilterButton, DashboardFilterButton,
DashboardRowsHeightButton,
FormattedMessage as T, FormattedMessage as T,
} from 'components'; } from 'components';
@@ -22,6 +23,8 @@ import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withManualJournalsActions from './withManualJournalsActions'; import withManualJournalsActions from './withManualJournalsActions';
import withManualJournals from './withManualJournals'; import withManualJournals from './withManualJournals';
import withSettingsActions from '../../Settings/withSettingsActions';
import withSettings from '../../Settings/withSettings';
import { If, DashboardActionViewsList } from 'components'; import { If, DashboardActionViewsList } from 'components';
@@ -36,6 +39,12 @@ function ManualJournalActionsBar({
// #withManualJournals // #withManualJournals
manualJournalsFilterConditions, manualJournalsFilterConditions,
// #withSettings
manualJournalsTableSize,
// #withSettingsActions
addSetting,
}) { }) {
// History context. // History context.
const history = useHistory(); const history = useHistory();
@@ -62,6 +71,11 @@ function ManualJournalActionsBar({
refresh(); refresh();
}; };
// Handle table row size change.
const handleTableRowSizeChange = (size) => {
addSetting('manualJournals', 'tableSize', size);
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -119,6 +133,12 @@ function ManualJournalActionsBar({
icon={<Icon icon="file-export-16" iconSize={16} />} icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />} text={<T id={'export'} />}
/> />
<NavbarDivider />
<DashboardRowsHeightButton
initialValue={manualJournalsTableSize}
onChange={handleTableRowSizeChange}
/>
<NavbarDivider />
</NavbarGroup> </NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}> <NavbarGroup align={Alignment.RIGHT}>
<Button <Button
@@ -134,7 +154,11 @@ function ManualJournalActionsBar({
export default compose( export default compose(
withDialogActions, withDialogActions,
withManualJournalsActions, withManualJournalsActions,
withSettingsActions,
withManualJournals(({ manualJournalsTableState }) => ({ withManualJournals(({ manualJournalsTableState }) => ({
manualJournalsFilterConditions: manualJournalsTableState.filterRoles, manualJournalsFilterConditions: manualJournalsTableState.filterRoles,
})), })),
withSettings(({ manualJournalsSettings }) => ({
manualJournalsTableSize: manualJournalsSettings?.tableSize,
})),
)(ManualJournalActionsBar); )(ManualJournalActionsBar);

View File

@@ -13,6 +13,7 @@ import withManualJournals from './withManualJournals';
import withManualJournalsActions from './withManualJournalsActions'; import withManualJournalsActions from './withManualJournalsActions';
import withAlertsActions from 'containers/Alert/withAlertActions'; import withAlertsActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions';
import withSettings from '../../Settings/withSettings';
import { useManualJournalsContext } from './ManualJournalsListProvider'; import { useManualJournalsContext } from './ManualJournalsListProvider';
import { useMemorizedColumnsWidths } from 'hooks'; import { useMemorizedColumnsWidths } from 'hooks';
@@ -38,6 +39,9 @@ function ManualJournalsDataTable({
// #ownProps // #ownProps
onSelectedRowsChange, onSelectedRowsChange,
// #withSettings
manualJournalsTableSize,
}) { }) {
// Manual journals context. // Manual journals context.
const { const {
@@ -109,7 +113,6 @@ function ManualJournalsDataTable({
data={manualJournals} data={manualJournals}
manualSortBy={true} manualSortBy={true}
selectionColumn={true} selectionColumn={true}
expandable={true}
sticky={true} sticky={true}
loading={isManualJournalsLoading} loading={isManualJournalsLoading}
headerLoading={isManualJournalsLoading} headerLoading={isManualJournalsLoading}
@@ -125,6 +128,7 @@ function ManualJournalsDataTable({
onCellClick={handleCellClick} onCellClick={handleCellClick}
initialColumnsWidths={initialColumnsWidths} initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing} onColumnResizing={handleColumnResizing}
size={manualJournalsTableSize}
payload={{ payload={{
onDelete: handleDeleteJournal, onDelete: handleDeleteJournal,
onPublish: handlePublishJournal, onPublish: handlePublishJournal,
@@ -143,4 +147,7 @@ export default compose(
})), })),
withAlertsActions, withAlertsActions,
withDrawerActions, withDrawerActions,
withSettings(({ manualJournalsSettings }) => ({
manualJournalsTableSize: manualJournalsSettings?.tableSize,
})),
)(ManualJournalsDataTable); )(ManualJournalsDataTable);

View File

@@ -37,6 +37,7 @@ export const useManualJournalsColumns = () => {
className: 'journal_number', className: 'journal_number',
width: 100, width: 100,
clickable: true, clickable: true,
textOverview: true,
}, },
{ {
id: 'journal_type', id: 'journal_type',
@@ -44,6 +45,7 @@ export const useManualJournalsColumns = () => {
accessor: 'journal_type', accessor: 'journal_type',
width: 110, width: 110,
clickable: true, clickable: true,
textOverview: true,
}, },
{ {
id: 'status', id: 'status',

View File

@@ -17,6 +17,7 @@ import {
If, If,
DashboardActionViewsList, DashboardActionViewsList,
DashboardFilterButton, DashboardFilterButton,
DashboardRowsHeightButton,
} from 'components'; } from 'components';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
@@ -27,7 +28,8 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
import withAccounts from 'containers/Accounts/withAccounts'; import withAccounts from 'containers/Accounts/withAccounts';
import withAlertActions from 'containers/Alert/withAlertActions'; import withAlertActions from 'containers/Alert/withAlertActions';
import withAccountsTableActions from './withAccountsTableActions'; import withAccountsTableActions from './withAccountsTableActions';
import withSettings from '../Settings/withSettings';
import withSettingsActions from '../Settings/withSettingsActions';
import { compose } from 'utils'; import { compose } from 'utils';
/** /**
@@ -50,6 +52,12 @@ function AccountsActionsBar({
// #ownProps // #ownProps
onFilterChanged, onFilterChanged,
// #withSettings
accountsTableSize,
// #withSettingsActions
addSetting,
}) { }) {
const { resourceViews, fields } = useAccountsChartContext(); const { resourceViews, fields } = useAccountsChartContext();
@@ -93,6 +101,10 @@ function AccountsActionsBar({
refresh(); refresh();
}; };
// Handle table row size change.
const handleTableRowSizeChange = (size) => {
addSetting('accounts', 'tableSize', size);
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -165,6 +177,12 @@ function AccountsActionsBar({
icon={<Icon icon="file-import-16" iconSize={16} />} icon={<Icon icon="file-import-16" iconSize={16} />}
text={<T id={'import'} />} text={<T id={'import'} />}
/> />
<NavbarDivider />
<DashboardRowsHeightButton
initialValue={accountsTableSize}
onChange={handleTableRowSizeChange}
/>
<NavbarDivider />
<Switch <Switch
labelElement={<T id={'inactive'} />} labelElement={<T id={'inactive'} />}
defaultChecked={accountsInactiveMode} defaultChecked={accountsInactiveMode}
@@ -185,10 +203,14 @@ function AccountsActionsBar({
export default compose( export default compose(
withDialogActions, withDialogActions,
withAlertActions, withAlertActions,
withSettingsActions,
withAccounts(({ accountsSelectedRows, accountsTableState }) => ({ withAccounts(({ accountsSelectedRows, accountsTableState }) => ({
accountsSelectedRows, accountsSelectedRows,
accountsInactiveMode: accountsTableState.inactiveMode, accountsInactiveMode: accountsTableState.inactiveMode,
accountsFilterConditions: accountsTableState.filterRoles, accountsFilterConditions: accountsTableState.filterRoles,
})), })),
withSettings(({ accountsSettings }) => ({
accountsTableSize: accountsSettings.tableSize,
})),
withAccountsTableActions, withAccountsTableActions,
)(AccountsActionsBar); )(AccountsActionsBar);

View File

@@ -10,6 +10,7 @@ import { TABLES } from 'common/tables';
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows'; import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows'; import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton'; import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import withSettings from '../Settings/withSettings';
import { useAccountsChartContext } from './AccountsChartProvider'; import { useAccountsChartContext } from './AccountsChartProvider';
import { useMemorizedColumnsWidths } from '../../hooks'; import { useMemorizedColumnsWidths } from '../../hooks';
@@ -30,6 +31,9 @@ function AccountsDataTable({
// #withDrawerActions // #withDrawerActions
openDrawer, openDrawer,
// #withSettings
accountsTableSize,
}) { }) {
const { isAccountsLoading, isAccountsFetching, accounts } = const { isAccountsLoading, isAccountsFetching, accounts } =
useAccountsChartContext(); useAccountsChartContext();
@@ -102,11 +106,12 @@ function AccountsDataTable({
TableHeaderSkeletonRenderer={TableSkeletonHeader} TableHeaderSkeletonRenderer={TableSkeletonHeader}
ContextMenu={ActionsMenu} ContextMenu={ActionsMenu}
// #TableVirtualizedListRows props. // #TableVirtualizedListRows props.
vListrowHeight={42} vListrowHeight={accountsTableSize == 'small' ? 40 : 42}
vListOverscanRowCount={0} vListOverscanRowCount={0}
onCellClick={handleCellClick} onCellClick={handleCellClick}
initialColumnsWidths={initialColumnsWidths} initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing} onColumnResizing={handleColumnResizing}
size={accountsTableSize}
payload={{ payload={{
onEdit: handleEditAccount, onEdit: handleEditAccount,
onDelete: handleDeleteAccount, onDelete: handleDeleteAccount,
@@ -123,4 +128,7 @@ export default compose(
withAlertsActions, withAlertsActions,
withDrawerActions, withDrawerActions,
withDialogActions, withDialogActions,
withSettings(({ accountsSettings }) => ({
accountsTableSize: accountsSettings.tableSize,
})),
)(AccountsDataTable); )(AccountsDataTable);

View File

@@ -35,7 +35,7 @@ function BillTransactionDeleteAlert({
deleteLandedCostMutate(BillId) deleteLandedCostMutate(BillId)
.then(() => { .then(() => {
AppToaster.show({ AppToaster.show({
message: intl.get('bill.action.delete_successfully.landed_cost'), message: intl.get('landed_cost.action.delete.success_message'),
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
closeAlert(name); closeAlert(name);
@@ -56,7 +56,11 @@ function BillTransactionDeleteAlert({
onConfirm={handleConfirmLandedCostDelete} onConfirm={handleConfirmLandedCostDelete}
loading={isLoading} loading={isLoading}
> >
<p><T id={`Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?`}/></p> <p>
<T
id={`Once your delete this located landed cost, you won't be able to restore it later, Are your sure you want to delete this transaction?`}
/>
</p>
</Alert> </Alert>
); );
} }

View File

@@ -31,7 +31,7 @@ function CustomerBulkDeleteAlert({
closeAlert(name); closeAlert(name);
}; };
console.log(customersIds, 'EE');
// Handle confirm customers bulk delete. // Handle confirm customers bulk delete.
const handleConfirmBulkDelete = useCallback(() => { const handleConfirmBulkDelete = useCallback(() => {

View File

@@ -1,17 +1,17 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { FormattedMessage as T, FormattedHTMLMessage } from 'components'; import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
import { Intent, Alert } from '@blueprintjs/core'; import { Intent, Alert } from '@blueprintjs/core';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { transformErrors } from 'containers/Customers/utils'; import { transformErrors } from 'containers/Customers/utils';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions'; import withAlertActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { useDeleteCustomer } from 'hooks/query'; import { useDeleteCustomer } from 'hooks/query';
import { compose } from 'utils'; import { compose } from 'utils';
/** /**
* Customer delete alert. * Customer delete alert.
*/ */
@@ -24,12 +24,11 @@ function CustomerDeleteAlert({
// #withAlertActions // #withAlertActions
closeAlert, closeAlert,
// #withDrawerActions
closeDrawer,
}) { }) {
const { mutateAsync: deleteCustomerMutate, isLoading } = useDeleteCustomer();
const {
mutateAsync: deleteCustomerMutate,
isLoading
} = useDeleteCustomer();
// handle cancel delete alert. // handle cancel delete alert.
const handleCancelDeleteAlert = () => { const handleCancelDeleteAlert = () => {
@@ -44,10 +43,17 @@ function CustomerDeleteAlert({
message: intl.get('the_customer_has_been_deleted_successfully'), message: intl.get('the_customer_has_been_deleted_successfully'),
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
closeDrawer('customer-details-drawer');
}) })
.catch(({ response: { data: { errors } } }) => { .catch(
transformErrors(errors); ({
}) response: {
data: { errors },
},
}) => {
transformErrors(errors);
},
)
.finally(() => { .finally(() => {
closeAlert(name); closeAlert(name);
}); });
@@ -76,4 +82,5 @@ function CustomerDeleteAlert({
export default compose( export default compose(
withAlertStoreConnect(), withAlertStoreConnect(),
withAlertActions, withAlertActions,
withDrawerActions,
)(CustomerDeleteAlert); )(CustomerDeleteAlert);

View File

@@ -46,7 +46,24 @@ function EstimateDeleteAlert({
}); });
closeDrawer('estimate-detail-drawer'); closeDrawer('estimate-detail-drawer');
}) })
.catch(({ errors }) => {}) .catch(
({
response: {
data: { errors },
},
}) => {
if (
errors.find((e) => e.type === 'SALE_ESTIMATE_CONVERTED_TO_INVOICE')
) {
AppToaster.show({
intent: Intent.DANGER,
message: intl.get(
'estimate.delete.error.estimate_converted_to_invoice',
),
});
}
},
)
.finally(() => { .finally(() => {
closeAlert(name); closeAlert(name);
}); });

View File

@@ -1,6 +1,6 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { FormattedMessage as T, FormattedHTMLMessage } from 'components'; import { FormattedMessage as T, FormattedHTMLMessage } from 'components';
import { Intent, Alert } from '@blueprintjs/core'; import { Intent, Alert } from '@blueprintjs/core';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
@@ -9,6 +9,7 @@ import { useDeleteVendor } from 'hooks/query';
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect'; import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
import withAlertActions from 'containers/Alert/withAlertActions'; import withAlertActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { compose } from 'utils'; import { compose } from 'utils';
@@ -24,8 +25,10 @@ function VendorDeleteAlert({
// #withAlertActions // #withAlertActions
closeAlert, closeAlert,
// #withDrawerActions
closeDrawer,
}) { }) {
const { mutateAsync: deleteVendorMutate, isLoading } = useDeleteVendor(); const { mutateAsync: deleteVendorMutate, isLoading } = useDeleteVendor();
// Handle cancel delete the vendor. // Handle cancel delete the vendor.
@@ -41,6 +44,7 @@ function VendorDeleteAlert({
message: intl.get('the_vendor_has_been_deleted_successfully'), message: intl.get('the_vendor_has_been_deleted_successfully'),
intent: Intent.SUCCESS, intent: Intent.SUCCESS,
}); });
closeDrawer('vendor-details-drawer');
}) })
.catch( .catch(
({ ({
@@ -79,4 +83,5 @@ function VendorDeleteAlert({
export default compose( export default compose(
withAlertStoreConnect(), withAlertStoreConnect(),
withAlertActions, withAlertActions,
withDrawerActions,
)(VendorDeleteAlert); )(VendorDeleteAlert);

View File

@@ -1,15 +0,0 @@
import React from 'react';
import * as R from 'ramda';
import withDashboardActions from '../../containers/Dashboard/withDashboardActions';
function AuthenticationBootJSX({ setAppIsLoading }) {
React.useEffect(() => {
setAppIsLoading(false);
}, [setAppIsLoading]);
return null;
}
export const AuthenticationBoot = R.compose(withDashboardActions)(
AuthenticationBootJSX,
);

View File

@@ -8,16 +8,17 @@ import {
Switch, Switch,
Alignment, Alignment,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import { import {
If, If,
Icon, Icon,
FormattedMessage as T,
DashboardActionViewsList, DashboardActionViewsList,
AdvancedFilterPopover, AdvancedFilterPopover,
DashboardFilterButton, DashboardFilterButton,
DashboardRowsHeightButton,
} from 'components'; } from 'components';
import { useCustomersListContext } from './CustomersListProvider'; import { useCustomersListContext } from './CustomersListProvider';
@@ -26,6 +27,8 @@ import { useRefreshCustomers } from 'hooks/query/customers';
import withCustomers from './withCustomers'; import withCustomers from './withCustomers';
import withCustomersActions from './withCustomersActions'; import withCustomersActions from './withCustomersActions';
import withAlertActions from 'containers/Alert/withAlertActions'; import withAlertActions from 'containers/Alert/withAlertActions';
import withSettingsActions from '../../Settings/withSettingsActions';
import withSettings from '../../Settings/withSettings';
import { compose } from 'utils'; import { compose } from 'utils';
@@ -43,6 +46,12 @@ function CustomerActionsBar({
// #withAlertActions // #withAlertActions
openAlert, openAlert,
// #withSettings
customersTableSize,
// #withSettingsActions
addSetting,
}) { }) {
// History context. // History context.
const history = useHistory(); const history = useHistory();
@@ -74,7 +83,14 @@ function CustomerActionsBar({
}; };
// Handle click a refresh customers // Handle click a refresh customers
const handleRefreshBtnClick = () => { refresh(); }; const handleRefreshBtnClick = () => {
refresh();
};
// Handle table row size change.
const handleTableRowSizeChange = (size) => {
addSetting('customers', 'tableSize', size);
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
@@ -130,6 +146,12 @@ function CustomerActionsBar({
icon={<Icon icon="file-export-16" iconSize={16} />} icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />} text={<T id={'export'} />}
/> />
<NavbarDivider />
<DashboardRowsHeightButton
initialValue={customersTableSize}
onChange={handleTableRowSizeChange}
/>
<NavbarDivider />
<Switch <Switch
labelElement={<T id={'inactive'} />} labelElement={<T id={'inactive'} />}
defaultChecked={accountsInactiveMode} defaultChecked={accountsInactiveMode}
@@ -149,10 +171,14 @@ function CustomerActionsBar({
export default compose( export default compose(
withCustomersActions, withCustomersActions,
withSettingsActions,
withCustomers(({ customersSelectedRows, customersTableState }) => ({ withCustomers(({ customersSelectedRows, customersTableState }) => ({
customersSelectedRows, customersSelectedRows,
accountsInactiveMode: customersTableState.inactiveMode, accountsInactiveMode: customersTableState.inactiveMode,
customersFilterConditions: customersTableState.filterRoles, customersFilterConditions: customersTableState.filterRoles,
})), })),
withSettings(({ customersSettings }) => ({
customersTableSize: customersSettings?.tableSize,
})),
withAlertActions, withAlertActions,
)(CustomerActionsBar); )(CustomerActionsBar);

View File

@@ -14,6 +14,7 @@ import withCustomersActions from './withCustomersActions';
import withAlertsActions from 'containers/Alert/withAlertActions'; import withAlertsActions from 'containers/Alert/withAlertActions';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions';
import withSettings from '../../Settings/withSettings';
import { useCustomersListContext } from './CustomersListProvider'; import { useCustomersListContext } from './CustomersListProvider';
import { useMemorizedColumnsWidths } from 'hooks'; import { useMemorizedColumnsWidths } from 'hooks';
@@ -38,6 +39,9 @@ function CustomersTable({
// #withDialogActions // #withDialogActions
openDialog, openDialog,
// #withSettings
customersTableSize,
}) { }) {
const history = useHistory(); const history = useHistory();
@@ -135,6 +139,7 @@ function CustomersTable({
onCellClick={handleCellClick} onCellClick={handleCellClick}
initialColumnsWidths={initialColumnsWidths} initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing} onColumnResizing={handleColumnResizing}
size={customersTableSize}
payload={{ payload={{
onDelete: handleCustomerDelete, onDelete: handleCustomerDelete,
onEdit: handleCustomerEdit, onEdit: handleCustomerEdit,
@@ -155,4 +160,7 @@ export default compose(
withCustomersActions, withCustomersActions,
withDrawerActions, withDrawerActions,
withCustomers(({ customersTableState }) => ({ customersTableState })), withCustomers(({ customersTableState }) => ({ customersTableState })),
withSettings(({ customersSettings }) => ({
customersTableSize: customersSettings?.tableSize,
})),
)(CustomersTable); )(CustomersTable);

View File

@@ -1,17 +1,12 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { import { Menu, MenuItem, MenuDivider, Intent } from '@blueprintjs/core';
Menu,
MenuItem,
MenuDivider,
Intent,
} from '@blueprintjs/core';
import clsx from 'classnames'; import clsx from 'classnames';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { CLASSES } from '../../../common/classes'; import { Icon, Money, If, AvaterCell } from 'components';
import { Icon, Money, If } from 'components';
import { } from 'utils'; import { safeCallback } from 'utils';
import { safeCallback, firstLettersArgs } from 'utils';
/** /**
* Actions menu. * Actions menu.
@@ -69,13 +64,6 @@ export function ActionsMenu({
); );
} }
/**
* Avatar cell.
*/
export function AvatarCell(row) {
return <span className="avatar">{firstLettersArgs(row.display_name)}</span>;
}
/** /**
* Phone number accessor. * Phone number accessor.
*/ */
@@ -99,7 +87,7 @@ export function useCustomersTableColumns() {
{ {
id: 'avatar', id: 'avatar',
Header: '', Header: '',
accessor: AvatarCell, Cell: AvaterCell,
className: 'avatar', className: 'avatar',
width: 45, width: 45,
disableResizing: true, disableResizing: true,

View File

@@ -11,8 +11,8 @@ export default (mapState) => {
sidebarExpended: state.dashboard.sidebarExpended, sidebarExpended: state.dashboard.sidebarExpended,
preferencesPageTitle: state.dashboard.preferencesPageTitle, preferencesPageTitle: state.dashboard.preferencesPageTitle,
dashboardBackLink: state.dashboard.backLink, dashboardBackLink: state.dashboard.backLink,
appIsLoading: state.dashboard.appIsLoading, splashScreenLoading: state.dashboard.splashScreenLoading > 0,
appIntlIsLoading: state.dashboard.appIntlIsLoading splashScreenCompleted: state.dashboard.splashScreenLoading === 0,
}; };
return mapState ? mapState(mapped, state, props) : mapped; return mapState ? mapState(mapped, state, props) : mapped;
}; };

View File

@@ -2,9 +2,8 @@ import { connect } from 'react-redux';
import t from 'store/types'; import t from 'store/types';
import { import {
toggleExpendSidebar, toggleExpendSidebar,
appIsLoading,
appIntlIsLoading
} from 'store/dashboard/dashboard.actions'; } from 'store/dashboard/dashboard.actions';
import { splashStartLoading, splashStopLoading } from '../../store/dashboard/dashboard.actions';
const mapActionsToProps = (dispatch) => ({ const mapActionsToProps = (dispatch) => ({
changePageTitle: (pageTitle) => changePageTitle: (pageTitle) =>
@@ -50,15 +49,17 @@ const mapActionsToProps = (dispatch) => ({
dispatch({ dispatch({
type: 'CHANGE_PREFERENCES_PAGE_TITLE', type: 'CHANGE_PREFERENCES_PAGE_TITLE',
pageTitle, pageTitle,
}), }),
setDashboardBackLink: (backLink) => setDashboardBackLink: (backLink) =>
dispatch({ dispatch({
type: t.SET_DASHBOARD_BACK_LINK, type: t.SET_DASHBOARD_BACK_LINK,
payload: { backLink }, payload: { backLink },
}), }),
setAppIsLoading: (isLoading) => dispatch(appIsLoading(isLoading)),
setAppIntlIsLoading: (isLoading) => dispatch(appIntlIsLoading(isLoading)), // Splash screen start/stop loading.
splashStartLoading: () => splashStartLoading(),
splashStopLoading: () => splashStopLoading(),
}); });
export default connect(null, mapActionsToProps); export default connect(null, mapActionsToProps);

View File

@@ -21,7 +21,7 @@ export const transformErrors = (errors, { setFieldError }) => {
intl.get('payment_number_is_not_unique'), intl.get('payment_number_is_not_unique'),
); );
} }
if (getError('INVALID_PAYMENT_AMOUNT')) { if (getError('INVALID_BILL_PAYMENT_AMOUNT')) {
setFieldError( setFieldError(
'payment_amount', 'payment_amount',
intl.get('the_payment_amount_bigger_than_invoice_due_amount'), intl.get('the_payment_amount_bigger_than_invoice_due_amount'),

View File

@@ -16,7 +16,7 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions'; import withAlertsActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { Icon, FormattedMessage as T } from 'components'; import { If, Icon, FormattedMessage as T } from 'components';
import { safeCallback, compose } from 'utils'; import { safeCallback, compose } from 'utils';
@@ -32,7 +32,7 @@ function BillDetailActionsBar({
}) { }) {
const history = useHistory(); const history = useHistory();
const { billId } = useBillDrawerContext(); const { billId, bill } = useBillDrawerContext();
// Handle edit bill. // Handle edit bill.
const onEditBill = () => { const onEditBill = () => {
@@ -45,6 +45,11 @@ function BillDetailActionsBar({
openAlert('bill-delete', { billId }); openAlert('bill-delete', { billId });
}; };
// Handle quick bill payment .
const handleQuickBillPayment = () => {
openDialog('quick-payment-made', { billId });
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -55,6 +60,15 @@ function BillDetailActionsBar({
onClick={safeCallback(onEditBill)} onClick={safeCallback(onEditBill)}
/> />
<NavbarDivider /> <NavbarDivider />
<If condition={bill.is_open && !bill.is_fully_paid}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="quick-payment-16" iconSize={16} />}
text={<T id={'add_payment'} />}
onClick={handleQuickBillPayment}
/>
</If>
<NavbarDivider />
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon={'trash-16'} iconSize={16} />} icon={<Icon icon={'trash-16'} iconSize={16} />}

View File

@@ -61,8 +61,8 @@ function CustomerDetailsActionsBar({
const handleDeleteCustomer = () => { const handleDeleteCustomer = () => {
openAlert(`customer-delete`, { contactId: customerId }); openAlert(`customer-delete`, { contactId: customerId });
closeDrawer('customer-details-drawer');
}; };
const handleEditContact = () => { const handleEditContact = () => {
history.push(`/customers/${customerId}/edit`); history.push(`/customers/${customerId}/edit`);
closeDrawer('customer-details-drawer'); closeDrawer('customer-details-drawer');
@@ -108,7 +108,7 @@ function CustomerDetailsActionsBar({
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="pen-18" />} icon={<Icon icon="pen-18" />}
text={intl.get('edit_contact', { name: contact?.contact_service })} text={intl.get('customer.drawer.action.edit')}
onClick={handleEditContact} onClick={handleEditContact}
/> />
<NavbarDivider /> <NavbarDivider />

View File

@@ -19,12 +19,7 @@ function InventoryAdjustmentDetailDrawer({
payload: { inventoryId }, payload: { inventoryId },
}) { }) {
return ( return (
<Drawer <Drawer isOpen={isOpen} name={name} size={'750px'}>
isOpen={isOpen}
name={name}
style={{ minWidth: '700px', maxWidth: '900px' }}
size={'65%'}
>
<DrawerSuspense> <DrawerSuspense>
<InventoryAdjustmentDrawerContent inventoryId={inventoryId} /> <InventoryAdjustmentDrawerContent inventoryId={inventoryId} />
</DrawerSuspense> </DrawerSuspense>

View File

@@ -16,7 +16,7 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions'; import withAlertsActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions';
import { Icon, FormattedMessage as T } from 'components'; import { If, Icon, FormattedMessage as T } from 'components';
import { compose } from 'utils'; import { compose } from 'utils';
@@ -36,7 +36,7 @@ function InvoiceDetailActionsBar({
const history = useHistory(); const history = useHistory();
// Invoice detail drawer context. // Invoice detail drawer context.
const { invoiceId } = useInvoiceDetailDrawerContext(); const { invoiceId, invoice } = useInvoiceDetailDrawerContext();
// Handle edit sale invoice. // Handle edit sale invoice.
const handleEditInvoice = () => { const handleEditInvoice = () => {
@@ -54,6 +54,11 @@ function InvoiceDetailActionsBar({
openDialog('invoice-pdf-preview', { invoiceId }); openDialog('invoice-pdf-preview', { invoiceId });
}; };
// Handle quick payment invoice.
const handleQuickPaymentInvoice = () => {
openDialog('quick-payment-receive', { invoiceId });
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -64,6 +69,15 @@ function InvoiceDetailActionsBar({
onClick={handleEditInvoice} onClick={handleEditInvoice}
/> />
<NavbarDivider /> <NavbarDivider />
<If condition={invoice.is_delivered && !invoice.is_fully_paid}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="quick-payment-16" iconSize={16} />}
text={<T id={'add_payment'} />}
onClick={handleQuickPaymentInvoice}
/>
</If>
<NavbarDivider />
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="print-16" />} icon={<Icon icon="print-16" />}

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { useManualJournalDrawerContext } from './ManualJournalDrawerProvider'; import { useManualJournalDrawerContext } from './ManualJournalDrawerProvider';
import { FormatNumber } from '../../../components'; import { T, FormatNumber } from '../../../components';
/** /**
* Manual journal readonly details footer. * Manual journal readonly details footer.
@@ -15,7 +15,9 @@ export default function ManualJournalDrawerFooter() {
<div className="journal-drawer__content-footer"> <div className="journal-drawer__content-footer">
<div class="total-lines"> <div class="total-lines">
<div class="total-lines__line total-lines__line--subtotal"> <div class="total-lines__line total-lines__line--subtotal">
<div class="title">Subtotal</div> <div class="title">
<T id={'manual_journal.details.subtotal'} />
</div>
<div class="debit"> <div class="debit">
<FormatNumber value={amount} /> <FormatNumber value={amount} />
</div> </div>
@@ -24,7 +26,9 @@ export default function ManualJournalDrawerFooter() {
</div> </div>
</div> </div>
<div class="total-lines__line total-lines__line--total"> <div class="total-lines__line total-lines__line--total">
<div class="title">TOTAL</div> <div class="title">
<T id={'manual_journal.details.total'} />
</div>
<div class="debit">{formatted_amount}</div> <div class="debit">{formatted_amount}</div>
<div class="credit">{formatted_amount}</div> <div class="credit">{formatted_amount}</div>
</div> </div>

View File

@@ -43,7 +43,10 @@ export default function ManualJournalDrawerHeader() {
</DetailsMenu> </DetailsMenu>
<div class="journal-drawer__content-description"> <div class="journal-drawer__content-description">
<b class="title">Description</b>: {defaultTo(description, '')} <b class="title">
<T id={'manual_journal.details.description'} />
</b>
: {defaultTo(description, '—')}
</div> </div>
</div> </div>
); );

View File

@@ -34,7 +34,7 @@ function PaymentMadeDetailProvider({ paymentMadeId, ...props }) {
} = useTransactionsByReference( } = useTransactionsByReference(
{ {
reference_id: paymentMadeId, reference_id: paymentMadeId,
reference_type: 'paymentMade', reference_type: 'BillPayment',
}, },
{ enabled: !!paymentMadeId }, { enabled: !!paymentMadeId },
); );

View File

@@ -46,7 +46,6 @@ function VendorDetailsActionsBar({
// Handle delete vendor. // Handle delete vendor.
const onDeleteContact = () => { const onDeleteContact = () => {
openAlert(`vendor-delete`, { contactId: vendorId }); openAlert(`vendor-delete`, { contactId: vendorId });
closeDrawer('vendor-details-drawer');
}; };
const handleNewInvoiceClick = () => { const handleNewInvoiceClick = () => {
@@ -91,7 +90,7 @@ function VendorDetailsActionsBar({
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="pen-18" />} icon={<Icon icon="pen-18" />}
text={<T id={'vendor.drawer.action.edit_vendor'} />} text={<T id={'vendor.drawer.action.edit'} />}
onClick={safeCallback(onEditContact)} onClick={safeCallback(onEditContact)}
/> />
<NavbarDivider /> <NavbarDivider />

View File

@@ -6,7 +6,6 @@ import { Formik, Form } from 'formik';
import classNames from 'classnames'; import classNames from 'classnames';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import * as R from 'ramda';
import ExpenseFormHeader from './ExpenseFormHeader'; import ExpenseFormHeader from './ExpenseFormHeader';
import ExpenseFormBody from './ExpenseFormBody'; import ExpenseFormBody from './ExpenseFormBody';
@@ -25,8 +24,13 @@ import {
CreateExpenseFormSchema, CreateExpenseFormSchema,
EditExpenseFormSchema, EditExpenseFormSchema,
} from './ExpenseForm.schema'; } from './ExpenseForm.schema';
import { transformErrors, defaultExpense, transformToEditForm } from './utils'; import {
import { compose, orderingLinesIndexes } from 'utils'; transformErrors,
defaultExpense,
transformToEditForm,
transformFormValuesToRequest,
} from './utils';
import { compose } from 'utils';
/** /**
* Expense form. * Expense form.
@@ -79,15 +83,10 @@ function ExpenseForm({
}); });
return; return;
} }
// Filter expense entries that has no amount or expense account.
const categories = values.categories.filter(
(category) => category.amount && category.expense_account_id,
);
const form = { const form = {
...values, ...transformFormValuesToRequest(values),
publish: submitPayload.publish, publish: submitPayload.publish,
categories: R.compose(orderingLinesIndexes)(categories),
}; };
// Handle request success. // Handle request success.
const handleSuccess = (response) => { const handleSuccess = (response) => {

View File

@@ -1,4 +1,5 @@
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { Intent } from '@blueprintjs/core';
import moment from 'moment'; import moment from 'moment';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import * as R from 'ramda'; import * as R from 'ramda';
@@ -6,11 +7,14 @@ import {
defaultFastFieldShouldUpdate, defaultFastFieldShouldUpdate,
transformToForm, transformToForm,
repeatValue, repeatValue,
ensureEntriesHasEmptyLine ensureEntriesHasEmptyLine,
orderingLinesIndexes
} from 'utils'; } from 'utils';
const ERROR = { const ERROR = {
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED', EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED:
'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED',
}; };
// Transform API errors in toasts messages. // Transform API errors in toasts messages.
@@ -24,6 +28,14 @@ export const transformErrors = (errors, { setErrors }) => {
}), }),
); );
} }
if (hasError(ERROR.ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED)) {
setErrors(
AppToaster.show({
intent: Intent.DANGER,
message: 'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED',
}),
);
}
}; };
export const MIN_LINES_NUMBER = 4; export const MIN_LINES_NUMBER = 4;
@@ -93,3 +105,25 @@ export const accountsFieldShouldUpdate = (newProps, oldProps) => {
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };
/**
* Filter expense entries that has no amount or expense account.
*/
export const filterNonZeroEntries = (categories) => {
return categories.filter(
(category) => category.amount && category.expense_account_id,
);
}
/**
* Transformes the form values to request body.
*/
export const transformFormValuesToRequest = (values) => {
const categories = filterNonZeroEntries(values.categories);
return {
...values,
categories: R.compose(orderingLinesIndexes)(categories),
};
};

View File

@@ -9,16 +9,17 @@ import {
Alignment, Alignment,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { FormattedMessage as T } from 'components';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import { import {
If, If,
DashboardRowsHeightButton,
DashboardActionViewsList, DashboardActionViewsList,
DashboardFilterButton, DashboardFilterButton,
AdvancedFilterPopover, AdvancedFilterPopover,
FormattedMessage as T,
} from 'components'; } from 'components';
import { useRefreshExpenses } from 'hooks/query/expenses'; import { useRefreshExpenses } from 'hooks/query/expenses';
@@ -27,6 +28,9 @@ import { useExpensesListContext } from './ExpensesListProvider';
import withExpensesActions from './withExpensesActions'; import withExpensesActions from './withExpensesActions';
import withExpenses from './withExpenses'; import withExpenses from './withExpenses';
import withSettingsActions from '../../Settings/withSettingsActions';
import withSettings from '../../Settings/withSettings';
import { compose } from 'utils'; import { compose } from 'utils';
/** /**
@@ -38,6 +42,12 @@ function ExpensesActionsBar({
// #withExpenses // #withExpenses
expensesFilterConditions, expensesFilterConditions,
// #withSettings
expensesTableSize,
// #withSettingsActions
addSetting,
}) { }) {
// History context. // History context.
const history = useHistory(); const history = useHistory();
@@ -67,6 +77,11 @@ function ExpensesActionsBar({
const handleRefreshBtnClick = () => { const handleRefreshBtnClick = () => {
refresh(); refresh();
}; };
// Handle table row size change.
const handleTableRowSizeChange = (size) => {
addSetting('expenses', 'tableSize', size);
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
<NavbarGroup> <NavbarGroup>
@@ -124,6 +139,12 @@ function ExpensesActionsBar({
icon={<Icon icon="file-export-16" iconSize={16} />} icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />} text={<T id={'export'} />}
/> />
<NavbarDivider />
<DashboardRowsHeightButton
initialValue={expensesTableSize}
onChange={handleTableRowSizeChange}
/>
<NavbarDivider />
</NavbarGroup> </NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}> <NavbarGroup align={Alignment.RIGHT}>
<Button <Button
@@ -139,7 +160,11 @@ function ExpensesActionsBar({
export default compose( export default compose(
withDialogActions, withDialogActions,
withExpensesActions, withExpensesActions,
withSettingsActions,
withExpenses(({ expensesTableState }) => ({ withExpenses(({ expensesTableState }) => ({
expensesFilterConditions: expensesTableState.filterRoles, expensesFilterConditions: expensesTableState.filterRoles,
})), })),
withSettings(({ expenseSettings }) => ({
expensesTableSize: expenseSettings?.tableSize,
})),
)(ExpensesActionsBar); )(ExpensesActionsBar);

View File

@@ -16,6 +16,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withExpensesActions from './withExpensesActions'; import withExpensesActions from './withExpensesActions';
import withAlertsActions from 'containers/Alert/withAlertActions'; import withAlertsActions from 'containers/Alert/withAlertActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions';
import withSettings from '../../Settings/withSettings';
import { ActionsMenu, useExpensesTableColumns } from './components'; import { ActionsMenu, useExpensesTableColumns } from './components';
@@ -31,6 +32,9 @@ function ExpensesDataTable({
// #withAlertsActions // #withAlertsActions
openAlert, openAlert,
// #withSettings
expensesTableSize,
}) { }) {
// Expenses list context. // Expenses list context.
const { const {
@@ -119,6 +123,7 @@ function ExpensesDataTable({
onCellClick={handleCellClick} onCellClick={handleCellClick}
initialColumnsWidths={initialColumnsWidths} initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing} onColumnResizing={handleColumnResizing}
size={expensesTableSize}
payload={{ payload={{
onPublish: handlePublishExpense, onPublish: handlePublishExpense,
onDelete: handleDeleteExpense, onDelete: handleDeleteExpense,
@@ -135,4 +140,7 @@ export default compose(
withAlertsActions, withAlertsActions,
withDrawerActions, withDrawerActions,
withExpensesActions, withExpensesActions,
withSettings(({ expenseSettings }) => ({
expensesTableSize: expenseSettings?.tableSize,
})),
)(ExpensesDataTable); )(ExpensesDataTable);

View File

@@ -31,7 +31,7 @@ function BalanceSheet({
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'cash', basis: 'cash',
displayColumnsType: 'total', displayColumnsType: 'total',
accountsFilter: 'without-zero-balance', filterByOption: 'without-zero-balance',
}); });
// Handle re-fetch balance sheet after filter change. // Handle re-fetch balance sheet after filter change.

View File

@@ -2,7 +2,7 @@ import React from 'react';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy'; import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
import RadiosAccountingBasis from '../RadiosAccountingBasis'; import RadiosAccountingBasis from '../RadiosAccountingBasis';
import FinancialAccountsFilter from '../FinancialAccountsFilter'; import FinancialStatementsFilter from '../FinancialStatementsFilter';
/** /**
* Balance sheet header - General panal. * Balance sheet header - General panal.
@@ -12,7 +12,7 @@ export default function BalanceSheetHeaderGeneralTab({}) {
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<SelectDisplayColumnsBy /> <SelectDisplayColumnsBy />
<FinancialAccountsFilter <FinancialStatementsFilter
initialSelectedItem={'all-accounts'} initialSelectedItem={'all-accounts'}
/> />
<RadiosAccountingBasis key={'basis'} /> <RadiosAccountingBasis key={'basis'} />

View File

@@ -5,7 +5,7 @@ import moment from 'moment';
export const getBalanceSheetHeaderDefaultValues = () => { export const getBalanceSheetHeaderDefaultValues = () => {
return { return {
basic: 'cash', basic: 'cash',
accountsFilter: 'without-zero-balance', filterByOption: 'without-zero-balance',
displayColumnsType: 'total', displayColumnsType: 'total',
fromDate: moment().toDate(), fromDate: moment().toDate(),
toDate: moment().toDate(), toDate: moment().toDate(),
@@ -20,6 +20,6 @@ export const getBalanceSheetHeaderValidationSchema = () =>
.min(Yup.ref('fromDate')) .min(Yup.ref('fromDate'))
.required() .required()
.label(intl.get('toDate')), .label(intl.get('toDate')),
accountsFilter: Yup.string(), filterByOption: Yup.string(),
displayColumnsType: Yup.string(), displayColumnsType: Yup.string(),
}); });

View File

@@ -34,6 +34,7 @@ function CashFlowStatement({
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'cash', basis: 'cash',
displayColumnsType: 'total', displayColumnsType: 'total',
filterByOption: 'with-transactions',
}); });
// Handle refetch cash flow after filter change. // Handle refetch cash flow after filter change.

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import FinancialAccountsFilter from '../FinancialAccountsFilter'; import FinancialStatementsFilter from '../FinancialStatementsFilter';
import RadiosAccountingBasis from '../RadiosAccountingBasis'; import RadiosAccountingBasis from '../RadiosAccountingBasis';
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy'; import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
@@ -13,7 +13,7 @@ export default function CashFlowStatementHeaderGeneralPanel() {
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<SelectDisplayColumnsBy /> <SelectDisplayColumnsBy />
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} /> <FinancialStatementsFilter initialSelectedItem={'with-transactions'} />
<RadiosAccountingBasis key={'basis'} /> <RadiosAccountingBasis key={'basis'} />
</div> </div>
); );

View File

@@ -29,6 +29,7 @@ function CustomersBalanceSummary({
}) { }) {
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
asDate: moment().endOf('day').format('YYYY-MM-DD'), asDate: moment().endOf('day').format('YYYY-MM-DD'),
filterByOption: 'with-transactions',
}); });
// Handle re-fetch customers balance summary after filter change. // Handle re-fetch customers balance summary after filter change.

View File

@@ -5,6 +5,8 @@ import { Classes, FormGroup, Position, Checkbox } from '@blueprintjs/core';
import { ContactsMultiSelect, FormattedMessage as T } from 'components'; import { ContactsMultiSelect, FormattedMessage as T } from 'components';
import classNames from 'classnames'; import classNames from 'classnames';
import { Row, Col, FieldHint } from 'components'; import { Row, Col, FieldHint } from 'components';
import { filterCustomersOptions } from '../constants';
import { import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
@@ -12,6 +14,7 @@ import {
handleDateChange, handleDateChange,
} from 'utils'; } from 'utils';
import { useCustomersBalanceSummaryGeneralContext } from './CustomersBalanceSummaryGeneralProvider'; import { useCustomersBalanceSummaryGeneralContext } from './CustomersBalanceSummaryGeneralProvider';
import FinancialStatementsFilter from '../FinancialStatementsFilter';
/** /**
* Customers balance header - General panel - Content * Customers balance header - General panel - Content
@@ -65,6 +68,12 @@ export default function CustomersBalanceSummaryGeneralPanelContent() {
</Col> </Col>
</Row> </Row>
<FinancialStatementsFilter
items={filterCustomersOptions}
label={<T id={'customers.label_filter_customers'} />}
initialSelectedItem={'with-transactions'}
/>
<Row> <Row>
<Col xs={5}> <Col xs={5}>
<Field name={'customersIds'}> <Field name={'customersIds'}>
@@ -80,7 +89,7 @@ export default function CustomersBalanceSummaryGeneralPanelContent() {
<ContactsMultiSelect <ContactsMultiSelect
items={customers} items={customers}
onItemSelect={(contacts) => { onItemSelect={(contacts) => {
const customersIds = contacts.map(contact => contact.id); const customersIds = contacts.map((contact) => contact.id);
setFieldValue('customersIds', customersIds); setFieldValue('customersIds', customersIds);
}} }}
/> />

View File

@@ -26,7 +26,6 @@ function CustomersBalanceSummaryHeader({
// #withCustomersBalanceSummaryActions // #withCustomersBalanceSummaryActions
toggleCustomerBalanceFilterDrawer, toggleCustomerBalanceFilterDrawer,
}) { }) {
// validation schema. // validation schema.
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
asDate: Yup.date().required().label('asDate'), asDate: Yup.date().required().label('asDate'),
@@ -34,15 +33,20 @@ function CustomersBalanceSummaryHeader({
// Default form values. // Default form values.
const defaultValues = { const defaultValues = {
...pageFilter,
asDate: moment().toDate(), asDate: moment().toDate(),
customersIds: [], customersIds: [],
}; };
// Filter form initial values. // Filter form initial values.
const initialValues = transformToForm({ const initialValues = transformToForm(
...pageFilter, {
asDate: moment(pageFilter.asDate).toDate(), ...defaultValues,
}, defaultValues); ...pageFilter,
asDate: moment(pageFilter.asDate).toDate(),
},
defaultValues,
);
// handle form submit. // handle form submit.
const handleSubmit = (values, { setSubmitting }) => { const handleSubmit = (values, { setSubmitting }) => {

View File

@@ -30,6 +30,7 @@ function CustomersTransactions({
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
filterByOption: 'with-transactions',
}); });
const handleFilterSubmit = (filter) => { const handleFilterSubmit = (filter) => {

View File

@@ -30,22 +30,25 @@ function CustomersTransactionsHeader({
}) { }) {
// Default form values. // Default form values.
const defaultValues = { const defaultValues = {
...pageFilter,
fromDate: moment().toDate(), fromDate: moment().toDate(),
toDate: moment().toDate(), toDate: moment().toDate(),
customersIds: [], customersIds: [],
}; };
// Initial form values. // Initial form values.
const initialValues = transformToForm({ const initialValues = transformToForm(
...pageFilter, {
fromDate: moment(pageFilter.fromDate).toDate(), ...defaultValues,
toDate: moment(pageFilter.toDate).toDate(), ...pageFilter,
}, defaultValues); fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
},
defaultValues,
);
// Validation schema. // Validation schema.
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
fromDate: Yup.date() fromDate: Yup.date().required().label(intl.get('fromDate')),
.required()
.label(intl.get('fromDate')),
toDate: Yup.date() toDate: Yup.date()
.min(Yup.ref('fromDate')) .min(Yup.ref('fromDate'))
.required() .required()
@@ -59,7 +62,9 @@ function CustomersTransactionsHeader({
setSubmitting(false); setSubmitting(false);
}; };
// Handle drawer close action. // Handle drawer close action.
const handleDrawerClose = () => { toggleFilterDrawer(false); }; const handleDrawerClose = () => {
toggleFilterDrawer(false);
};
return ( return (
<FinancialStatementHeader <FinancialStatementHeader

View File

@@ -3,12 +3,15 @@ import classNames from 'classnames';
import { Field } from 'formik'; import { Field } from 'formik';
import { Classes, FormGroup } from '@blueprintjs/core'; import { Classes, FormGroup } from '@blueprintjs/core';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import FinancialStatementsFilter from '../FinancialStatementsFilter';
import { import {
Row, Row,
Col, Col,
ContactsMultiSelect, ContactsMultiSelect,
FormattedMessage as T, FormattedMessage as T,
} from '../../../components'; } from '../../../components';
import { filterCustomersOptions } from '../constants';
import { import {
CustomersTransactionsGeneralPanelProvider, CustomersTransactionsGeneralPanelProvider,
useCustomersTransactionsGeneralPanelContext, useCustomersTransactionsGeneralPanelContext,
@@ -34,7 +37,11 @@ function CustomersTransactionsHeaderGeneralPanelContent() {
return ( return (
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<FinancialStatementsFilter
items={filterCustomersOptions}
label={<T id={'customers.label_filter_customers'} />}
initialSelectedItem={'with-transactions'}
/>
<Row> <Row>
<Col xs={5}> <Col xs={5}>
<Field name={'customersIds'}> <Field name={'customersIds'}>

View File

@@ -6,7 +6,8 @@ import { Row, Col, Hint } from 'components';
import { momentFormatter, parseDateRangeQuery } from 'utils'; import { momentFormatter, parseDateRangeQuery } from 'utils';
import { DateInput } from '@blueprintjs/datetime'; import { DateInput } from '@blueprintjs/datetime';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { dateRangeOptions } from 'containers/FinancialStatements/common'; import { dateRangeOptions } from './constants';
/** /**
* Financial statement - Date range select. * Financial statement - Date range select.

View File

@@ -12,17 +12,21 @@ import { FastField } from 'formik';
import { CLASSES } from 'common/classes'; import { CLASSES } from 'common/classes';
import { Col, Row, ListSelect, MODIFIER } from 'components'; import { Col, Row, ListSelect, MODIFIER } from 'components';
import { filterAccountsOptions } from './common'; import { filterAccountsOptions } from './constants';
export default function FinancialAccountsFilter({ ...restProps }) { export default function FinancialStatementsFilter({
items = filterAccountsOptions,
label = <T id={'filter_accounts'} />,
...restProps
}) {
const SUBMENU_POPOVER_MODIFIERS = { const SUBMENU_POPOVER_MODIFIERS = {
flip: { boundariesElement: 'viewport', padding: 20 }, flip: { boundariesElement: 'viewport', padding: 20 },
offset: { offset: '0, 10' }, offset: { offset: '0, 10' },
preventOverflow: { boundariesElement: 'viewport', padding: 40 }, preventOverflow: { boundariesElement: 'viewport', padding: 40 },
}; };
const filterAccountRenderer = (item, { handleClick, modifiers, query }) => { const filterRenderer = (item, { handleClick, modifiers, query }) => {
return ( return (
<Tooltip <Tooltip
interactionKind={PopoverInteractionKind.HOVER} interactionKind={PopoverInteractionKind.HOVER}
@@ -41,23 +45,23 @@ export default function FinancialAccountsFilter({ ...restProps }) {
return ( return (
<Row> <Row>
<Col xs={4}> <Col xs={4}>
<FastField name={'accountsFilter'}> <FastField name={'filterByOption'}>
{({ form: { setFieldValue }, field: { value } }) => ( {({ form: { setFieldValue }, field: { value } }) => (
<FormGroup <FormGroup
label={<T id={'filter_accounts'} />} label={label}
className="form-group--select-list bp3-fill" className="form-group--select-list bp3-fill"
inline={false} inline={false}
> >
<ListSelect <ListSelect
items={filterAccountsOptions} items={items}
itemRenderer={filterAccountRenderer} itemRenderer={filterRenderer}
popoverProps={{ minimal: true }} popoverProps={{ minimal: true }}
filterable={false} filterable={false}
selectedItem={value} selectedItem={value}
selectedItemProp={'key'} selectedItemProp={'key'}
textProp={'name'} textProp={'name'}
onItemSelect={(item) => { onItemSelect={(item) => {
setFieldValue('accountsFilter', item.key); setFieldValue('filterByOption', item.key);
}} }}
className={classNames(CLASSES.SELECT_LIST_FILL_POPOVER)} className={classNames(CLASSES.SELECT_LIST_FILL_POPOVER)}
{...restProps} {...restProps}

View File

@@ -34,7 +34,7 @@ function GeneralLedger({
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accural',
accountsFilter: 'with-transactions', filterByOption: 'with-transactions',
}); });
// Handle financial statement filter change. // Handle financial statement filter change.

View File

@@ -7,7 +7,7 @@ import { AccountsMultiSelect, Row, Col } from 'components';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import RadiosAccountingBasis from '../RadiosAccountingBasis'; import RadiosAccountingBasis from '../RadiosAccountingBasis';
import FinancialAccountsFilter from '../FinancialAccountsFilter'; import FinancialStatementsFilter from '../FinancialStatementsFilter';
import { GLHeaderGeneralPanelProvider } from './GLHeaderGeneralPaneProvider'; import { GLHeaderGeneralPanelProvider } from './GLHeaderGeneralPaneProvider';
import { filterAccountsOptions } from './common'; import { filterAccountsOptions } from './common';
@@ -33,9 +33,9 @@ function GLHeaderGeneralPaneContent() {
return ( return (
<React.Fragment> <React.Fragment>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<FinancialAccountsFilter <FinancialStatementsFilter
items={filterAccountsOptions} items={filterAccountsOptions}
initialSelectedItem={'all-accounts'} initialSelectedItem={'with-transactions'}
/> />
<Row> <Row>
<Col xs={4}> <Col xs={4}>

View File

@@ -27,6 +27,7 @@ function InventoryValuation({
}) { }) {
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
asDate: moment().endOf('day').format('YYYY-MM-DD'), asDate: moment().endOf('day').format('YYYY-MM-DD'),
filterByOption: 'with-transactions',
}); });
// Handle filter form submit. // Handle filter form submit.
@@ -54,7 +55,6 @@ function InventoryValuation({
[toggleInventoryValuationFilterDrawer], [toggleInventoryValuationFilterDrawer],
); );
return ( return (
<InventoryValuationProvider query={filter}> <InventoryValuationProvider query={filter}>
<InventoryValuationActionsBar <InventoryValuationActionsBar

View File

@@ -33,12 +33,14 @@ function InventoryValuationHeader({
// Default values. // Default values.
const defaultValues = { const defaultValues = {
...pageFilter,
asDate: moment().toDate(), asDate: moment().toDate(),
itemsIds: [], itemsIds: [],
}; };
// Initial values. // Initial values.
const initialValues = transformToForm({ const initialValues = transformToForm({
...pageFilter, ...pageFilter,
...defaultValues,
asDate: moment(pageFilter.asDate).toDate(), asDate: moment(pageFilter.asDate).toDate(),
}, defaultValues); }, defaultValues);

View File

@@ -11,6 +11,8 @@ import {
Col, Col,
FieldHint, FieldHint,
} from '../../../components'; } from '../../../components';
import { filterInventoryValuationOptions } from '../constants';
import { import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
@@ -21,6 +23,7 @@ import {
InventoryValuationGeneralPanelProvider, InventoryValuationGeneralPanelProvider,
useInventoryValuationGeneralPanelContext, useInventoryValuationGeneralPanelContext,
} from './InventoryValuationHeaderGeneralPanelProvider'; } from './InventoryValuationHeaderGeneralPanelProvider';
import FinancialStatementsFilter from '../FinancialStatementsFilter';
/** /**
* Inventory valuation - Drawer Header - General panel. * Inventory valuation - Drawer Header - General panel.
@@ -42,7 +45,7 @@ function InventoryValuationHeaderGeneralPanelContent() {
return ( return (
<div> <div>
<Row> <Row>
<Col xs={5}> <Col xs={4}>
<FastField name={'asDate'}> <FastField name={'asDate'}>
{({ form, field: { value }, meta: { error } }) => ( {({ form, field: { value }, meta: { error } }) => (
<FormGroup <FormGroup
@@ -66,9 +69,14 @@ function InventoryValuationHeaderGeneralPanelContent() {
</FastField> </FastField>
</Col> </Col>
</Row> </Row>
<FinancialStatementsFilter
items={filterInventoryValuationOptions}
label={<T id={'items.label_filter_items'} />}
initialSelectedItem={'all-items'}
/>
<Row> <Row>
<Col xs={5}> <Col xs={4}>
<Field name={'itemsIds'}> <Field name={'itemsIds'}>
{({ form: { setFieldValue } }) => ( {({ form: { setFieldValue } }) => (
<FormGroup <FormGroup

View File

@@ -14,10 +14,7 @@ import withCurrentOrganization from '../../Organization/withCurrentOrganization'
import 'style/pages/FinancialStatements/ProfitLossSheet.scss'; import 'style/pages/FinancialStatements/ProfitLossSheet.scss';
import { ProfitLossSheetProvider } from './ProfitLossProvider'; import { ProfitLossSheetProvider } from './ProfitLossProvider';
import { import { ProfitLossSheetLoadingBar, ProfitLossSheetAlerts } from './components';
ProfitLossSheetLoadingBar,
ProfitLossSheetAlerts
} from './components';
/** /**
* Profit/Loss financial statement sheet. * Profit/Loss financial statement sheet.
@@ -34,7 +31,7 @@ function ProfitLossSheet({
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
displayColumnsType: 'total', displayColumnsType: 'total',
accountsFilter: 'all-accounts', filterByOption: 'with-transactions',
}); });
// Handle submit filter. // Handle submit filter.

View File

@@ -25,18 +25,14 @@ function ProfitLossHeader({
// #withProfitLossActions // #withProfitLossActions
toggleProfitLossFilterDrawer: toggleFilterDrawer, toggleProfitLossFilterDrawer: toggleFilterDrawer,
}) { }) {
// Validation schema. // Validation schema.
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
fromDate: Yup.date() fromDate: Yup.date().required().label(intl.get('from_date')),
.required()
.label(intl.get('from_date')),
toDate: Yup.date() toDate: Yup.date()
.min(Yup.ref('fromDate')) .min(Yup.ref('fromDate'))
.required() .required()
.label(intl.get('to_date')), .label(intl.get('to_date')),
accountsFilter: Yup.string(), filterByOption: Yup.string(),
displayColumnsType: Yup.string(), displayColumnsType: Yup.string(),
}); });

View File

@@ -3,7 +3,7 @@ import React from 'react';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy'; import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
import RadiosAccountingBasis from '../RadiosAccountingBasis'; import RadiosAccountingBasis from '../RadiosAccountingBasis';
import FinancialAccountsFilter from '../FinancialAccountsFilter'; import FinancialStatementsFilter from '../FinancialStatementsFilter';
/** /**
* Profit/Loss sheet - Drawer header - General panel. * Profit/Loss sheet - Drawer header - General panel.
@@ -13,7 +13,7 @@ export default function ProfitLossSheetHeaderGeneralPane({}) {
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<SelectDisplayColumnsBy /> <SelectDisplayColumnsBy />
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} /> <FinancialStatementsFilter initialSelectedItem={'with-transactions'} />
<RadiosAccountingBasis key={'basis'} /> <RadiosAccountingBasis key={'basis'} />
</div> </div>
); );

View File

@@ -27,6 +27,7 @@ function PurchasesByItems({
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
filterByOption: 'with-transactions',
}); });
// Handle filter form submit. // Handle filter form submit.

View File

@@ -9,6 +9,8 @@ import {
} from '../../../components'; } from '../../../components';
import classNames from 'classnames'; import classNames from 'classnames';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import FinancialStatementsFilter from '../FinancialStatementsFilter';
import { filterItemsOptions } from '../constants';
import { import {
PurchasesByItemsGeneralPanelProvider, PurchasesByItemsGeneralPanelProvider,
@@ -35,6 +37,11 @@ function PurchasesByItemsGeneralPanelContent() {
return ( return (
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<FinancialStatementsFilter
items={filterItemsOptions}
label={<T id={'items.label_filter_items'} />}
initialSelectedItem={'with-transactions'}
/>
<Row> <Row>
<Col xs={4}> <Col xs={4}>

View File

@@ -47,6 +47,7 @@ function PurchasesByItemsHeader({
// Initial form values. // Initial form values.
const initialValues = transformToForm( const initialValues = transformToForm(
{ {
...defaultValues,
...pageFilter, ...pageFilter,
fromDate: moment(pageFilter.fromDate).toDate(), fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(), toDate: moment(pageFilter.toDate).toDate(),

View File

@@ -1,7 +1,6 @@
import React, { useEffect, useState, useCallback } from 'react'; import React, { useEffect, useState, useCallback } from 'react';
import moment from 'moment'; import moment from 'moment';
import 'style/pages/FinancialStatements/SalesAndPurchasesSheet.scss'; import 'style/pages/FinancialStatements/SalesAndPurchasesSheet.scss';
import { SalesByItemProvider } from './SalesByItemProvider'; import { SalesByItemProvider } from './SalesByItemProvider';
@@ -30,6 +29,7 @@ function SalesByItems({
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
filterByOption: 'with-transactions',
}); });
// Handle filter form submit. // Handle filter form submit.

View File

@@ -2,10 +2,11 @@ import React from 'react';
import { FormGroup, Classes } from '@blueprintjs/core'; import { FormGroup, Classes } from '@blueprintjs/core';
import { Field } from 'formik'; import { Field } from 'formik';
import classNames from 'classnames'; import classNames from 'classnames';
import { get } from 'lodash'; import { filterItemsOptions } from '../constants';
import { Row, Col, ItemsMultiSelect, FormattedMessage as T } from 'components'; import { Row, Col, ItemsMultiSelect, FormattedMessage as T } from 'components';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import FinancialStatementsFilter from '../FinancialStatementsFilter';
import { import {
SalesByItemGeneralPanelProvider, SalesByItemGeneralPanelProvider,
useSalesByItemsGeneralPanelContext, useSalesByItemsGeneralPanelContext,
@@ -32,6 +33,12 @@ function SalesByItemsHeaderGeneralPanelContent() {
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<FinancialStatementsFilter
items={filterItemsOptions}
label={<T id={'items.label_filter_items'} />}
initialSelectedItem={'with-transactions'}
/>
<Row> <Row>
<Col xs={4}> <Col xs={4}>
<Field name={'itemsIds'}> <Field name={'itemsIds'}>

View File

@@ -3,7 +3,7 @@ import { FormGroup } from '@blueprintjs/core';
import { FastField } from 'formik'; import { FastField } from 'formik';
import { FormattedMessage as T } from 'components'; import { FormattedMessage as T } from 'components';
import { Row, Col, ListSelect } from 'components'; import { Row, Col, ListSelect } from 'components';
import { displayColumnsByOptions } from 'containers/FinancialStatements/common'; import { displayColumnsByOptions } from './constants';
/** /**
* Financial statement - Display columns by and type select. * Financial statement - Display columns by and type select.

View File

@@ -33,7 +33,7 @@ function TrialBalanceSheet({
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
basis: 'accural', basis: 'accural',
accountsFilter: 'all-accounts', filterByOption: 'with-transactions',
}); });
// Handle filter form submit. // Handle filter form submit.

View File

@@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange'; import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
import RadiosAccountingBasis from '../RadiosAccountingBasis'; import RadiosAccountingBasis from '../RadiosAccountingBasis';
import FinancialAccountsFilter from '../FinancialAccountsFilter'; import FinancialStatementsFilter from '../FinancialStatementsFilter';
/** /**
* Trial balance sheet - Drawer header - General panel. * Trial balance sheet - Drawer header - General panel.
@@ -12,7 +12,7 @@ export default function TrialBalanceSheetHeaderGeneralPanel({
return ( return (
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} /> <FinancialStatementsFilter initialSelectedItem={'with-transactions'} />
<RadiosAccountingBasis /> <RadiosAccountingBasis />
</div> </div>
); );

View File

@@ -30,6 +30,7 @@ function VendorsBalanceSummary({
}) { }) {
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
asDate: moment().endOf('day').format('YYYY-MM-DD'), asDate: moment().endOf('day').format('YYYY-MM-DD'),
filterByOption: 'with-transactions',
}); });
// Handle refetch vendors balance summary. // Handle refetch vendors balance summary.

View File

@@ -33,14 +33,20 @@ function VendorsBalanceSummaryHeader({
// filter form initial values. // filter form initial values.
const defaultValues = { const defaultValues = {
...pageFilter,
asDate: moment().toDate(), asDate: moment().toDate(),
vendorsIds: [], vendorsIds: [],
}; };
// Initial form values. // Initial form values.
const initialValues = transformToForm({ const initialValues = transformToForm(
...pageFilter, {
asDate: moment(pageFilter.asDate).toDate(), ...defaultValues,
}, defaultValues);
...pageFilter,
asDate: moment(pageFilter.asDate).toDate(),
},
defaultValues,
);
// handle form submit. // handle form submit.
const handleSubmit = (values, { setSubmitting }) => { const handleSubmit = (values, { setSubmitting }) => {

View File

@@ -11,6 +11,8 @@ import {
FieldHint, FieldHint,
FormattedMessage as T, FormattedMessage as T,
} from '../../../components'; } from '../../../components';
import { filterVendorsOptions } from '../constants';
import { import {
momentFormatter, momentFormatter,
tansformDateValue, tansformDateValue,
@@ -18,6 +20,7 @@ import {
handleDateChange, handleDateChange,
} from 'utils'; } from 'utils';
import { useVendorsBalanceSummaryGeneralPanelContext } from './VendorsBalanceSummaryHeaderGeneralProvider'; import { useVendorsBalanceSummaryGeneralPanelContext } from './VendorsBalanceSummaryHeaderGeneralProvider';
import FinancialStatementsFilter from '../FinancialStatementsFilter';
/** /**
* Vendors balance header - General panel - Content. * Vendors balance header - General panel - Content.
@@ -71,6 +74,12 @@ export default function VendorsBalanceSummaryHeaderGeneralContent() {
</Col> </Col>
</Row> </Row>
<FinancialStatementsFilter
items={filterVendorsOptions}
label={<T id={'vendors.label_filter_vendors'} />}
initialSelectedItem={'with-transactions'}
/>
<Row> <Row>
<Col xs={4}> <Col xs={4}>
<Field name={'vendorsIds'}> <Field name={'vendorsIds'}>

View File

@@ -31,6 +31,7 @@ function VendorsTransactions({
const [filter, setFilter] = useState({ const [filter, setFilter] = useState({
fromDate: moment().startOf('year').format('YYYY-MM-DD'), fromDate: moment().startOf('year').format('YYYY-MM-DD'),
toDate: moment().endOf('year').format('YYYY-MM-DD'), toDate: moment().endOf('year').format('YYYY-MM-DD'),
filterByOption: 'with-transactions',
}); });
const handleFilterSubmit = (filter) => { const handleFilterSubmit = (filter) => {

View File

@@ -31,23 +31,26 @@ function VendorsTransactionsHeader({
}) { }) {
// Default form values. // Default form values.
const defaultValues = { const defaultValues = {
...pageFilter,
fromDate: moment().toDate(), fromDate: moment().toDate(),
toDate: moment().toDate(), toDate: moment().toDate(),
vendorsIds: [], vendorsIds: [],
}; };
// Initial form values. // Initial form values.
const initialValues = transformToForm({ const initialValues = transformToForm(
...pageFilter, {
fromDate: moment(pageFilter.fromDate).toDate(), ...defaultValues,
toDate: moment(pageFilter.toDate).toDate(), ...pageFilter,
}, defaultValues); fromDate: moment(pageFilter.fromDate).toDate(),
toDate: moment(pageFilter.toDate).toDate(),
},
defaultValues,
);
// Validation schema. // Validation schema.
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
fromDate: Yup.date() fromDate: Yup.date().required().label(intl.get('fromDate')),
.required()
.label(intl.get('fromDate')),
toDate: Yup.date() toDate: Yup.date()
.min(Yup.ref('fromDate')) .min(Yup.ref('fromDate'))
.required() .required()
@@ -62,7 +65,9 @@ function VendorsTransactionsHeader({
}; };
// Handle drawer close action. // Handle drawer close action.
const handleDrawerClose = () => { toggleFilterDrawer(false); }; const handleDrawerClose = () => {
toggleFilterDrawer(false);
};
return ( return (
<FinancialStatementHeader <FinancialStatementHeader

View File

@@ -4,12 +4,16 @@ import classNames from 'classnames';
import { Classes, FormGroup } from '@blueprintjs/core'; import { Classes, FormGroup } from '@blueprintjs/core';
import FinancialStatementDateRange from '../FinancialStatementDateRange'; import FinancialStatementDateRange from '../FinancialStatementDateRange';
import FinancialStatementsFilter from '../FinancialStatementsFilter';
import { import {
Row, Row,
Col, Col,
ContactsMultiSelect, ContactsMultiSelect,
FormattedMessage as T, FormattedMessage as T,
} from '../../../components'; } from '../../../components';
import { filterVendorsOptions } from '../constants';
import { import {
VendorsTransactionsGeneralPanelProvider, VendorsTransactionsGeneralPanelProvider,
useVendorsTransactionsGeneralPanelContext, useVendorsTransactionsGeneralPanelContext,
@@ -35,7 +39,11 @@ function VendorsTransactionsHeaderGeneralPanelContent() {
return ( return (
<div> <div>
<FinancialStatementDateRange /> <FinancialStatementDateRange />
<FinancialStatementsFilter
items={filterVendorsOptions}
label={<T id={'vendors.label_filter_vendors'} />}
initialSelectedItem={'all-vendors'}
/>
<Row> <Row>
<Col xs={5}> <Col xs={5}>
<Field name={'vendorsIds'}> <Field name={'vendorsIds'}>

View File

@@ -1,72 +1,9 @@
import * as R from 'ramda'; import * as R from 'ramda';
import intl from 'react-intl-universal'; import { displayColumnsByOptions } from './constants';
import { transfromToSnakeCase, flatten } from 'utils'; import { transfromToSnakeCase, flatten } from 'utils';
export const displayColumnsByOptions = [
{ key: 'total', name: intl.get('total'), type: 'total', by: '' },
{
key: 'year',
name: intl.get('date_year'),
type: 'date_periods',
by: 'year',
},
{
key: 'month',
name: intl.get('date_month'),
type: 'date_periods',
by: 'month',
},
{
key: 'week',
name: intl.get('date_week'),
type: 'date_periods',
by: 'month',
},
{
key: 'day',
name: intl.get('date_day'),
type: 'date_periods',
by: 'day',
},
{
key: 'quarter',
name: intl.get('date_quarter'),
type: 'date_periods',
by: 'quarter',
},
];
export const dateRangeOptions = [
{ value: 'today', label: intl.get('today') },
{ value: 'this_week', label: intl.get('this_week') },
{ value: 'this_month', label: intl.get('this_month') },
{ value: 'this_quarter', label: intl.get('this_quarter') },
{ value: 'this_year', label: intl.get('this_year') },
{ value: 'custom', label: intl.get('custom_range') },
];
export const filterAccountsOptions = [
{
key: 'all-accounts',
name: intl.get('all_accounts'),
hint: intl.get('all_accounts_including_with_zero_balance'),
},
{
key: 'without-zero-balance',
name: intl.get('accounts_without_zero_balance'),
hint: intl.get('include_accounts_and_exclude_zero_balance'),
},
{
key: 'with-transactions',
name: intl.get('accounts_with_transactions'),
hint: intl.get(
'include_accounts_once_has_transactions_on_given_date_period',
),
},
];
/** /**
* Associate display columns by and type properties to query object. * Associate display columns by and type properties to query object.
*/ */
export const transformDisplayColumnsType = (form) => { export const transformDisplayColumnsType = (form) => {
const columnType = displayColumnsByOptions.find( const columnType = displayColumnsByOptions.find(
@@ -85,17 +22,15 @@ export const transformDisplayColumnsType = (form) => {
const setNoneZeroTransactions = (form) => { const setNoneZeroTransactions = (form) => {
return { return {
...form, ...form,
noneZero: form.accountsFilter === 'without-zero-balance', noneZero: form.filterByOption === 'without-zero-balance',
noneTransactions: form.accountsFilter === 'with-transactions', noneTransactions: form.filterByOption === 'with-transactions',
onlyActive: form.filterByOption === 'with-only-active',
}; };
} };
// filterByOption
export const transformAccountsFilter = (form) => { export const transformAccountsFilter = (form) => {
return R.compose( return R.compose(R.omit(['filterByOption']), setNoneZeroTransactions)(form);
R.omit(['accountsFilter']), };
setNoneZeroTransactions,
)(form)
}
/** /**
* Transform filter form to http query. * Transform filter form to http query.

View File

@@ -0,0 +1,141 @@
import intl from 'react-intl-universal';
export const displayColumnsByOptions = [
{ key: 'total', name: intl.get('total'), type: 'total', by: '' },
{
key: 'year',
name: intl.get('date_year'),
type: 'date_periods',
by: 'year',
},
{
key: 'month',
name: intl.get('date_month'),
type: 'date_periods',
by: 'month',
},
{
key: 'week',
name: intl.get('date_week'),
type: 'date_periods',
by: 'month',
},
{
key: 'day',
name: intl.get('date_day'),
type: 'date_periods',
by: 'day',
},
{
key: 'quarter',
name: intl.get('date_quarter'),
type: 'date_periods',
by: 'quarter',
},
];
export const dateRangeOptions = [
{ value: 'today', label: intl.get('today') },
{ value: 'this_week', label: intl.get('this_week') },
{ value: 'this_month', label: intl.get('this_month') },
{ value: 'this_quarter', label: intl.get('this_quarter') },
{ value: 'this_year', label: intl.get('this_year') },
{ value: 'custom', label: intl.get('custom_range') },
];
export const filterAccountsOptions = [
{
key: 'all-accounts',
name: intl.get('all_accounts'),
hint: intl.get('all_accounts_including_with_zero_balance'),
},
{
key: 'without-zero-balance',
name: intl.get('accounts_without_zero_balance'),
hint: intl.get('include_accounts_and_exclude_zero_balance'),
},
{
key: 'with-transactions',
name: intl.get('accounts_with_transactions'),
hint: intl.get(
'include_accounts_once_has_transactions_on_given_date_period',
),
},
];
export const filterItemsOptions = [
{
key: 'all-items',
name: intl.get('all_items'),
hint: intl.get('items.option_all_items.hint'),
},
{
key: 'with-transactions',
name: intl.get('items.option_with_transactions'),
hint: intl.get('items.option_with_transactions.hint'),
},
{
key: 'with-only-active',
name: intl.get('items.option.only_active'),
},
];
export const filterCustomersOptions = [
{
key: 'all-customers',
name: intl.get('all_customers'),
hint: intl.get('customers.option_all_customers.hint'),
},
{
key: 'without-zero-balance',
name: intl.get('customers.option_without_zero_balance'),
hint: intl.get('customers.option_without_zero_balance.hint'),
},
{
key: 'with-transactions',
name: intl.get('customers.option_with_transactions'),
hint: intl.get('customers.option_with_transactions.hint'),
},
];
export const filterVendorsOptions = [
{
key: 'all-vendors',
name: intl.get('all_vendors'),
hint: intl.get('vendors.option_all_vendors.hint'),
},
{
key: 'without-zero-balance',
name: intl.get('vendors.option_without_zero_balance'),
hint: intl.get('vendors.option_without_zero_balance.hint'),
},
{
key: 'with-transactions',
name: intl.get('vendors.option_with_transactions'),
hint: intl.get('vendors.option_with_transactions.hint'),
},
];
export const filterInventoryValuationOptions = [
{
key: 'all-items',
name: intl.get('all_items'),
hint: intl.get('items.option_all_items.hint'),
},
{
key: 'with-transactions',
name: intl.get('items.option_with_transactions'),
hint: intl.get('items.option_with_transactions.hint'),
},
{
key: 'without-zero-balance',
name: intl.get('items.option_without_zero_balance'),
hint: intl.get('items.option_without_zero_balance.hint'),
},
{
key: 'with-only-active',
name: intl.get('items.option.only_active'),
},
]

View File

@@ -9,7 +9,7 @@ import {
Switch, Switch,
Alignment, Alignment,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components'; import { DashboardRowsHeightButton, FormattedMessage as T } from 'components';
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar'; import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
import Icon from 'components/Icon'; import Icon from 'components/Icon';
import { import {
@@ -25,8 +25,10 @@ import { useRefreshItems } from 'hooks/query/items';
import withItems from 'containers/Items/withItems'; import withItems from 'containers/Items/withItems';
import withItemsActions from './withItemsActions'; import withItemsActions from './withItemsActions';
import withAlertActions from 'containers/Alert/withAlertActions'; import withAlertActions from 'containers/Alert/withAlertActions';
import withSettings from '../Settings/withSettings';
import { compose } from 'utils'; import { compose } from 'utils';
import withSettingsActions from '../Settings/withSettingsActions';
/** /**
* Items actions bar. * Items actions bar.
@@ -42,6 +44,12 @@ function ItemsActionsBar({
// #withAlertActions // #withAlertActions
openAlert, openAlert,
// #withSettings
itemsTableSize,
// #withSettingsActions
addSetting,
}) { }) {
// Items list context. // Items list context.
const { itemsViews, fields } = useItemsListContext(); const { itemsViews, fields } = useItemsListContext();
@@ -72,10 +80,14 @@ function ItemsActionsBar({
const checked = event.target.checked; const checked = event.target.checked;
setItemsTableState({ inactiveMode: checked }); setItemsTableState({ inactiveMode: checked });
}; };
// Handle refresh button click.
const handleRefreshBtnClick = () => { const handleRefreshBtnClick = () => {
refresh(); refresh();
}; };
// Handle table row size change.
const handleTableRowSizeChange = (size) => {
addSetting('items', 'tableSize', size);
};
return ( return (
<DashboardActionsBar> <DashboardActionsBar>
@@ -131,6 +143,12 @@ function ItemsActionsBar({
icon={<Icon icon="file-export-16" iconSize={16} />} icon={<Icon icon="file-export-16" iconSize={16} />}
text={<T id={'export'} />} text={<T id={'export'} />}
/> />
<NavbarDivider />
<DashboardRowsHeightButton
initialValue={itemsTableSize}
onChange={handleTableRowSizeChange}
/>
<NavbarDivider />
<Switch <Switch
labelElement={<T id={'inactive'} />} labelElement={<T id={'inactive'} />}
defaultChecked={itemsInactiveMode} defaultChecked={itemsInactiveMode}
@@ -150,11 +168,15 @@ function ItemsActionsBar({
} }
export default compose( export default compose(
withSettingsActions,
withItems(({ itemsSelectedRows, itemsTableState }) => ({ withItems(({ itemsSelectedRows, itemsTableState }) => ({
itemsSelectedRows, itemsSelectedRows,
itemsInactiveMode: itemsTableState.inactiveMode, itemsInactiveMode: itemsTableState.inactiveMode,
itemsFilterRoles: itemsTableState.filterRoles, itemsFilterRoles: itemsTableState.filterRoles,
})), })),
withSettings(({ itemsSettings }) => ({
itemsTableSize: itemsSettings.tableSize,
})),
withItemsActions, withItemsActions,
withAlertActions, withAlertActions,
)(ItemsActionsBar); )(ItemsActionsBar);

View File

@@ -10,11 +10,11 @@ import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
import { TABLES } from 'common/tables'; import { TABLES } from 'common/tables';
import withItems from 'containers/Items/withItems';
import withItemsActions from 'containers/Items/withItemsActions'; import withItemsActions from 'containers/Items/withItemsActions';
import withAlertsActions from 'containers/Alert/withAlertActions'; import withAlertsActions from 'containers/Alert/withAlertActions';
import withDialogActions from 'containers/Dialog/withDialogActions'; import withDialogActions from 'containers/Dialog/withDialogActions';
import withDrawerActions from 'containers/Drawer/withDrawerActions'; import withDrawerActions from 'containers/Drawer/withDrawerActions';
import withSettings from '../Settings/withSettings';
import { useItemsListContext } from './ItemsListProvider'; import { useItemsListContext } from './ItemsListProvider';
import { useItemsTableColumns, ItemsActionMenuList } from './components'; import { useItemsTableColumns, ItemsActionMenuList } from './components';
@@ -37,8 +37,8 @@ function ItemsDataTable({
// #withDrawerActions // #withDrawerActions
openDrawer, openDrawer,
// #withItems // #withSettings
itemsTableState, itemsTableSize,
// #ownProps // #ownProps
tableProps, tableProps,
@@ -146,6 +146,7 @@ function ItemsDataTable({
onCellClick={handleCellClick} onCellClick={handleCellClick}
initialColumnsWidths={initialColumnsWidths} initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing} onColumnResizing={handleColumnResizing}
size={itemsTableSize}
payload={{ payload={{
onDeleteItem: handleDeleteItem, onDeleteItem: handleDeleteItem,
onEditItem: handleEditItem, onEditItem: handleEditItem,
@@ -167,5 +168,7 @@ export default compose(
withAlertsActions, withAlertsActions,
withDrawerActions, withDrawerActions,
withDialogActions, withDialogActions,
withItems(({ itemsTableState }) => ({ itemsTableState })), withSettings(({ itemsSettings }) => ({
itemsTableSize: itemsSettings.tableSize,
})),
)(ItemsDataTable); )(ItemsDataTable);

View File

@@ -34,7 +34,7 @@ const transfromItemsToSearch = (item) => ({
id: item.id, id: item.id,
text: item.name, text: item.name,
subText: item.code, subText: item.code,
label: item.type, label: item.type_formatted,
reference: item, reference: item,
}); });

View File

@@ -50,7 +50,7 @@ function ItemsCategoryActionsBar({
}); });
}; };
console.log(fields, categoriesFilterConditions, 'XXXX');
return ( return (
<DashboardActionsBar> <DashboardActionsBar>

View File

@@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
import { Formik, Form } from 'formik'; import { Formik, Form } from 'formik';
import { Intent } from '@blueprintjs/core'; import { Intent } from '@blueprintjs/core';
import classNames from 'classnames'; import classNames from 'classnames';
import * as R from 'ramda';
import intl from 'react-intl-universal'; import intl from 'react-intl-universal';
import { useHistory } from 'react-router-dom'; import { useHistory } from 'react-router-dom';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
@@ -16,13 +15,14 @@ import BillItemsEntriesEditor from './BillItemsEntriesEditor';
import { AppToaster } from 'components'; import { AppToaster } from 'components';
import { ERROR } from 'common/errors';
import { useBillFormContext } from './BillFormProvider'; import { useBillFormContext } from './BillFormProvider';
import { compose, safeSumBy } from 'utils'; import { compose, safeSumBy } from 'utils';
import { import {
defaultBill, defaultBill,
filterNonZeroEntries,
transformToEditForm, transformToEditForm,
transformEntriesToSubmit, transformFormValuesToRequest,
handleErrors,
} from './utils'; } from './utils';
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization'; import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
@@ -55,23 +55,12 @@ function BillForm({
[bill, base_currency], [bill, base_currency],
); );
// Transform response error to fields.
const handleErrors = (errors, { setErrors }) => {
if (errors.some((e) => e.type === ERROR.BILL_NUMBER_EXISTS)) {
setErrors({
bill_number: intl.get('bill_number_exists'),
});
}
};
// Handles form submit. // Handles form submit.
const handleFormSubmit = ( const handleFormSubmit = (
values, values,
{ setSubmitting, setErrors, resetForm }, { setSubmitting, setErrors, resetForm },
) => { ) => {
const entries = values.entries.filter( const entries = filterNonZeroEntries(values.entries);
(item) => item.item_id && item.quantity,
);
const totalQuantity = safeSumBy(entries, 'quantity'); const totalQuantity = safeSumBy(entries, 'quantity');
if (totalQuantity === 0) { if (totalQuantity === 0) {
@@ -83,9 +72,8 @@ function BillForm({
return; return;
} }
const form = { const form = {
...values, ...transformFormValuesToRequest(values),
open: submitPayload.status, open: submitPayload.status,
entries: transformEntriesToSubmit(entries),
}; };
// Handle the request success. // Handle the request success.
const onSuccess = (response) => { const onSuccess = (response) => {

View File

@@ -41,6 +41,12 @@ export const defaultBill = {
entries: [...repeatValue(defaultBillEntry, MIN_LINES_NUMBER)], entries: [...repeatValue(defaultBillEntry, MIN_LINES_NUMBER)],
}; };
export const ERRORS = {
// Bills
BILL_NUMBER_EXISTS: 'BILL.NUMBER.EXISTS',
ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED:
'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED',
};
/** /**
* Transformes the bill to initial values of edit form. * Transformes the bill to initial values of edit form.
*/ */
@@ -70,11 +76,33 @@ export const transformToEditForm = (bill) => {
* Transformes bill entries to submit request. * Transformes bill entries to submit request.
*/ */
export const transformEntriesToSubmit = (entries) => { export const transformEntriesToSubmit = (entries) => {
const transformBillEntry = R.curry(transformToForm)(R.__, defaultBillEntry); const transformBillEntry = R.compose(
R.omit(['amount']),
R.curry(transformToForm)(R.__, defaultBillEntry),
);
return R.compose(orderingLinesIndexes, R.map(transformBillEntry))(entries); return R.compose(orderingLinesIndexes, R.map(transformBillEntry))(entries);
}; };
/**
* Filters the givne non-zero entries.
*/
export const filterNonZeroEntries = (entries) => {
return entries.filter((item) => item.item_id && item.quantity);
};
/**
* Transformes form values to request body.
*/
export const transformFormValuesToRequest = (values) => {
const entries = filterNonZeroEntries(values.entries);
return {
...values,
entries: transformEntriesToSubmit(entries),
open: false,
};
};
/** /**
* Handle delete errors. * Handle delete errors.
*/ */
@@ -118,3 +146,24 @@ export const entriesFieldShouldUpdate = (newProps, oldProps) => {
defaultFastFieldShouldUpdate(newProps, oldProps) defaultFastFieldShouldUpdate(newProps, oldProps)
); );
}; };
// Transform response error to fields.
export const handleErrors = (errors, { setErrors }) => {
if (errors.some((e) => e.type === ERRORS.BILL_NUMBER_EXISTS)) {
setErrors({
bill_number: intl.get('bill_number_exists'),
});
}
if (
errors.some(
(e) => e.type === ERRORS.ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED,
)
) {
setErrors(
AppToaster.show({
intent: Intent.DANGER,
message: 'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED',
}),
);
}
};

Some files were not shown because too many files have changed in this diff Show More