mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-12 10:50:31 +00:00
Compare commits
47 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77f0a767b3 | ||
|
|
8a982e5c7e | ||
|
|
2f7564eb9c | ||
|
|
7c2c362585 | ||
|
|
786aad438a | ||
|
|
d6c78a9908 | ||
|
|
0aca6d9af7 | ||
|
|
696943153d | ||
|
|
2e437d7b65 | ||
|
|
5b12c4a433 | ||
|
|
96269ccafb | ||
|
|
f556f061cb | ||
|
|
b98e8aeeb4 | ||
|
|
17d5bbd9d1 | ||
|
|
e1ab4e4d65 | ||
|
|
b86a3a19dc | ||
|
|
3b2796cb6d | ||
|
|
15ee32f6a4 | ||
|
|
90e550c902 | ||
|
|
cbc0ccbfb9 | ||
|
|
526c46b24d | ||
|
|
6041c175fd | ||
|
|
0f58665a0d | ||
|
|
91036c3e52 | ||
|
|
555e3a2434 | ||
|
|
b87e85d5ff | ||
|
|
288225a0c1 | ||
|
|
71f9fa47d4 | ||
|
|
fcace4213c | ||
|
|
31d2b1b09a | ||
|
|
cd5116dbcb | ||
|
|
010b660318 | ||
|
|
4e99607b06 | ||
|
|
a3f1857e91 | ||
|
|
460ee2718e | ||
|
|
87938b8f41 | ||
|
|
cd70bf1d80 | ||
|
|
c4f2ea405c | ||
|
|
d1cb7eb51b | ||
|
|
e949b1b0c7 | ||
|
|
9b7382e222 | ||
|
|
364859a793 | ||
|
|
fd07306102 | ||
|
|
5fc4897663 | ||
|
|
7b85dfee5d | ||
|
|
14012c4d7e | ||
|
|
ecf56f3b99 |
@@ -6,6 +6,15 @@ WORKDIR /app
|
||||
|
||||
COPY ./package.json /app/package.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
|
||||
|
||||
|
||||
17639
package-lock.json
generated
17639
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,8 +4,8 @@
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "7.8.4",
|
||||
"@blueprintjs/core": "^3.38.1",
|
||||
"@blueprintjs/datetime": "^3.15.2",
|
||||
"@blueprintjs/core": "^3.50.2",
|
||||
"@blueprintjs/datetime": "^3.23.12",
|
||||
"@blueprintjs/popover2": "^0.11.1",
|
||||
"@blueprintjs/select": "^3.11.2",
|
||||
"@blueprintjs/table": "^3.8.3",
|
||||
|
||||
@@ -13,3 +13,9 @@ export const TABLES = {
|
||||
MANUAL_JOURNALS: 'manual_journal',
|
||||
EXPENSES: 'expenses',
|
||||
};
|
||||
|
||||
export const TABLE_SIZE = {
|
||||
COMPACT: 'compact',
|
||||
SMALL: 'small',
|
||||
MEDIUM: 'medium',
|
||||
}
|
||||
@@ -4,8 +4,9 @@ import { DateInput } from '@blueprintjs/datetime';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import { isUndefined } from 'lodash';
|
||||
|
||||
import { useAutofocus } from 'hooks';
|
||||
import { Choose, ListSelect } from 'components';
|
||||
import { T, Choose, ListSelect } from 'components';
|
||||
import { momentFormatter } from 'utils';
|
||||
|
||||
function AdvancedFilterEnumerationField({ options, value, ...rest }) {
|
||||
@@ -19,7 +20,7 @@ function AdvancedFilterEnumerationField({ options, value, ...rest }) {
|
||||
minimal: true,
|
||||
captureDismiss: true,
|
||||
}}
|
||||
defaultText={`Select an option`}
|
||||
defaultText={<T id={'filter.select_option'} />}
|
||||
textProp={'label'}
|
||||
selectedItemProp={'key'}
|
||||
{...rest}
|
||||
@@ -32,8 +33,7 @@ const IFieldType = {
|
||||
BOOLEAN: 'boolean',
|
||||
NUMBER: 'number',
|
||||
DATE: 'date',
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
function tansformDateValue(date, defaultValue = null) {
|
||||
return date ? moment(date).toDate() : defaultValue;
|
||||
@@ -46,13 +46,13 @@ export default function AdvancedFilterValueField2({
|
||||
fieldType,
|
||||
options,
|
||||
onChange,
|
||||
isFocus
|
||||
isFocus,
|
||||
}) {
|
||||
const [localValue, setLocalValue] = React.useState(value);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (localValue !== value && !isUndefined(value)) {
|
||||
setLocalValue(value)
|
||||
setLocalValue(value);
|
||||
}
|
||||
}, [localValue, value]);
|
||||
|
||||
@@ -106,10 +106,10 @@ export default function AdvancedFilterValueField2({
|
||||
position: Position.BOTTOM,
|
||||
}}
|
||||
shortcuts={true}
|
||||
placeholder={'Enter date'}
|
||||
placeholder={intl.get('filter.enter_date')}
|
||||
fill={true}
|
||||
inputProps={{
|
||||
fill: true
|
||||
fill: true,
|
||||
}}
|
||||
/>
|
||||
</Choose.When>
|
||||
@@ -120,7 +120,7 @@ export default function AdvancedFilterValueField2({
|
||||
|
||||
<Choose.Otherwise>
|
||||
<InputGroup
|
||||
placeholder={intl.get('value')}
|
||||
placeholder={intl.get('filter.value')}
|
||||
onChange={handleInputChange}
|
||||
value={localValue}
|
||||
inputRef={valueRef}
|
||||
@@ -128,4 +128,4 @@ export default function AdvancedFilterValueField2({
|
||||
</Choose.Otherwise>
|
||||
</Choose>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,15 +6,36 @@ import { ReactQueryDevtools } from 'react-query/devtools';
|
||||
|
||||
import 'style/App.scss';
|
||||
import 'moment/locale/ar-ly';
|
||||
import 'moment/locale/es-us'
|
||||
import 'moment/locale/es-us';
|
||||
|
||||
import AppIntlLoader from './AppIntlLoader';
|
||||
import PrivateRoute from 'components/Guards/PrivateRoute';
|
||||
import GlobalErrors from 'containers/GlobalErrors/GlobalErrors';
|
||||
import DashboardPrivatePages from 'components/Dashboard/PrivatePages';
|
||||
import Authentication from 'components/Authentication';
|
||||
|
||||
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.
|
||||
@@ -31,21 +52,10 @@ export default function App() {
|
||||
<SplashScreen />
|
||||
|
||||
<AppIntlLoader>
|
||||
<div className="App">
|
||||
<Router history={history}>
|
||||
<Switch>
|
||||
<Route path={'/auth'} component={Authentication} />
|
||||
<Route path={'/'}>
|
||||
<PrivateRoute component={DashboardPrivatePages} />
|
||||
</Route>
|
||||
</Switch>
|
||||
</Router>
|
||||
|
||||
<GlobalErrors />
|
||||
</div>
|
||||
<AppInsider history={history} />
|
||||
</AppIntlLoader>
|
||||
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,10 @@ import rtlDetect from 'rtl-detect';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import { AppIntlProvider } from './AppIntlProvider';
|
||||
import { useSplashLoading } from '../hooks/state';
|
||||
|
||||
import { useWatch } from '../hooks';
|
||||
import withDashboardActions from '../containers/Dashboard/withDashboardActions';
|
||||
import withDashboard from '../containers/Dashboard/withDashboard';
|
||||
|
||||
const SUPPORTED_LOCALES = [
|
||||
{ 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 }) {
|
||||
const [isLocalsLoading, setIsLocalsLoading] = React.useState(true);
|
||||
const [isYupLoading, setIsYupLoading] = 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);
|
||||
function useAppLoadLocales(currentLocale) {
|
||||
const [startLoading, stopLoading] = useSplashLoading();
|
||||
const [isLoading, setIsLoading] = React.useState(true);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Lodas the locales data file.
|
||||
@@ -91,33 +85,72 @@ function AppIntlLoader({ appIntlIsLoading, setAppIntlIsLoading, children }) {
|
||||
})
|
||||
.then(() => {
|
||||
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(() => {
|
||||
loadYupLocales(currentLocale)
|
||||
.then(({ locale }) => {
|
||||
setLocale(locale);
|
||||
setIsYupLoading(false);
|
||||
setIsLoading(false);
|
||||
})
|
||||
.then(() => {});
|
||||
}, [currentLocale]);
|
||||
}, [currentLocale, stopLoading]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!isLocalsLoading && !isYupLoading) {
|
||||
setAppIntlIsLoading(false);
|
||||
}
|
||||
// Watches the valiue to start/stop splash screen.
|
||||
useWatch(isLoading, (value) => (value ? startLoading() : stopLoading()), {
|
||||
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 (
|
||||
<AppIntlProvider currentLocale={currentLocale} isRTL={isRTL}>
|
||||
{appIntlIsLoading ? null : children}
|
||||
{isLoading ? null : children}
|
||||
</AppIntlProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default R.compose(
|
||||
withDashboardActions,
|
||||
withDashboard(({ appIntlIsLoading }) => ({ appIntlIsLoading })),
|
||||
)(AppIntlLoader);
|
||||
export default R.compose(withDashboardActions)(AppIntlLoader);
|
||||
|
||||
@@ -10,6 +10,7 @@ function AppIntlProvider({ currentLocale, isRTL, children }) {
|
||||
currentLocale,
|
||||
isRTL,
|
||||
isLTR: !isRTL,
|
||||
direction: isRTL ? 'rtl' : 'ltr',
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,7 +6,6 @@ import authenticationRoutes from 'routes/authentication';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import Icon from 'components/Icon';
|
||||
import { useIsAuthenticated } from 'hooks/state';
|
||||
import {AuthenticationBoot} from '../containers/Authentication/AuthenticationBoot';
|
||||
import 'style/pages/Authentication/Auth.scss';
|
||||
|
||||
function PageFade(props) {
|
||||
@@ -26,7 +25,6 @@ export default function AuthenticationWrapper({ ...rest }) {
|
||||
) : (
|
||||
<BodyClassName className={'authentication'}>
|
||||
<div class="authentication-page">
|
||||
<AuthenticationBoot />
|
||||
<a
|
||||
href={'http://bigcapital.ly'}
|
||||
className={'authentication-page__goto-bigcapital'}
|
||||
|
||||
10
src/components/AvaterCell.js
Normal file
10
src/components/AvaterCell.js
Normal 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>
|
||||
);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
|
||||
|
||||
export default function Card({ className, children }) {
|
||||
return <div className={classNames('card', className)}>{children}</div>;
|
||||
}
|
||||
|
||||
@@ -1,23 +1,30 @@
|
||||
import React from 'react';
|
||||
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 withDashboardActions from '../../containers/Dashboard/withDashboardActions';
|
||||
|
||||
import { setCookie, getCookie } from '../../utils';
|
||||
|
||||
/**
|
||||
* Dashboard async booting.
|
||||
*/
|
||||
function DashboardBootJSX({ setAppIsLoading, authenticatedUserId }) {
|
||||
function DashboardBootJSX({ authenticatedUserId }) {
|
||||
// Fetches the current user's organization.
|
||||
const { isSuccess: isCurrentOrganizationSuccess, data: organization } =
|
||||
useCurrentOrganization();
|
||||
const {
|
||||
isSuccess: isCurrentOrganizationSuccess,
|
||||
isLoading: isOrgLoading,
|
||||
data: organization,
|
||||
} = useCurrentOrganization();
|
||||
|
||||
// Authenticated user.
|
||||
const { isSuccess: isAuthUserSuccess, data: authUser } =
|
||||
useUser(authenticatedUserId);
|
||||
const {
|
||||
isSuccess: isAuthUserSuccess,
|
||||
isLoading: isAuthUserLoading,
|
||||
} = useUser(authenticatedUserId);
|
||||
|
||||
// Initial locale cookie value.
|
||||
const localeCookie = getCookie('locale');
|
||||
@@ -48,23 +55,39 @@ function DashboardBootJSX({ setAppIsLoading, authenticatedUserId }) {
|
||||
}
|
||||
}, [localeCookie, organization]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// Once the all requests complete change the app loading state.
|
||||
if (
|
||||
isAuthUserSuccess &&
|
||||
const [startLoading, stopLoading] = useSplashLoading();
|
||||
|
||||
// Splash loading when organization request loading and
|
||||
// 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 &&
|
||||
localeCookie === organization?.metadata?.language
|
||||
) {
|
||||
setAppIsLoading(false);
|
||||
localeCookie === organization?.metadata?.language,
|
||||
() => {
|
||||
isBooted.current = true;
|
||||
}
|
||||
}, [
|
||||
isAuthUserSuccess,
|
||||
isCurrentOrganizationSuccess,
|
||||
organization,
|
||||
setAppIsLoading,
|
||||
localeCookie,
|
||||
]);
|
||||
},
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -72,5 +95,4 @@ export const DashboardBoot = R.compose(
|
||||
withAuthentication(({ authenticatedUserId }) => ({
|
||||
authenticatedUserId,
|
||||
})),
|
||||
withDashboardActions,
|
||||
)(DashboardBootJSX);
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
70
src/components/Dashboard/DashboardRowsHeightButton/index.js
Normal file
70
src/components/Dashboard/DashboardRowsHeightButton/index.js
Normal 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',
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
.menu{
|
||||
:global .bp3-heading{
|
||||
font-weight: 400;
|
||||
opacity: 0.5;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.button{
|
||||
min-width: 34px;
|
||||
}
|
||||
@@ -4,10 +4,9 @@ import { Switch, Route } from 'react-router';
|
||||
import Dashboard from 'components/Dashboard/Dashboard';
|
||||
import SetupWizardPage from 'containers/Setup/WizardSetupPage';
|
||||
|
||||
import EnsureOrganizationIsReady from 'components/Guards/EnsureOrganizationIsReady';
|
||||
import EnsureOrganizationIsNotReady from 'components/Guards/EnsureOrganizationIsNotReady';
|
||||
import EnsureOrganizationIsReady from '../../components/Guards/EnsureOrganizationIsReady';
|
||||
import EnsureOrganizationIsNotReady from '../../components/Guards/EnsureOrganizationIsNotReady';
|
||||
import { PrivatePagesProvider } from './PrivatePagesProvider';
|
||||
import { DashboardBoot } from '../../components';
|
||||
|
||||
import 'style/pages/Dashboard/Dashboard.scss';
|
||||
|
||||
@@ -17,8 +16,6 @@ import 'style/pages/Dashboard/Dashboard.scss';
|
||||
export default function DashboardPrivatePages() {
|
||||
return (
|
||||
<PrivatePagesProvider>
|
||||
<DashboardBoot />
|
||||
|
||||
<Switch>
|
||||
<Route path={'/setup'}>
|
||||
<EnsureOrganizationIsNotReady>
|
||||
|
||||
@@ -1,9 +1,31 @@
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import { AuthenticatedUser } from './AuthenticatedUser';
|
||||
import { DashboardBoot } from '../../components';
|
||||
|
||||
import withDashboard from '../../containers/Dashboard/withDashboard';
|
||||
|
||||
/**
|
||||
* Private pages provider.
|
||||
*/
|
||||
export function PrivatePagesProvider({ children }) {
|
||||
return <AuthenticatedUser>{children}</AuthenticatedUser>;
|
||||
function PrivatePagesProviderComponent({
|
||||
splashScreenCompleted,
|
||||
|
||||
// #ownProps
|
||||
children,
|
||||
}) {
|
||||
return (
|
||||
<AuthenticatedUser>
|
||||
<DashboardBoot />
|
||||
|
||||
{splashScreenCompleted ? children : null}
|
||||
</AuthenticatedUser>
|
||||
);
|
||||
}
|
||||
|
||||
export const PrivatePagesProvider = R.compose(
|
||||
withDashboard(({ splashScreenCompleted }) => ({
|
||||
splashScreenCompleted,
|
||||
})),
|
||||
)(PrivatePagesProviderComponent);
|
||||
|
||||
@@ -3,13 +3,12 @@ import * as R from 'ramda';
|
||||
import BigcapitalLoading from './BigcapitalLoading';
|
||||
import withDashboard from '../../containers/Dashboard/withDashboard';
|
||||
|
||||
function SplashScreenComponent({ appIsLoading, appIntlIsLoading }) {
|
||||
return appIsLoading || appIntlIsLoading ? <BigcapitalLoading /> : null;
|
||||
function SplashScreenComponent({ splashScreenLoading }) {
|
||||
return splashScreenLoading ? <BigcapitalLoading /> : null;
|
||||
}
|
||||
|
||||
export const SplashScreen = R.compose(
|
||||
withDashboard(({ appIsLoading, appIntlIsLoading }) => ({
|
||||
appIsLoading,
|
||||
appIntlIsLoading,
|
||||
withDashboard(({ splashScreenLoading }) => ({
|
||||
splashScreenLoading,
|
||||
})),
|
||||
)(SplashScreenComponent);
|
||||
|
||||
@@ -196,6 +196,9 @@ export default function DataTable(props) {
|
||||
|
||||
DataTable.defaultProps = {
|
||||
pagination: false,
|
||||
hidePaginationNoPages: true,
|
||||
|
||||
size: null,
|
||||
spinnerProps: { size: 30 },
|
||||
|
||||
expandToggleColumn: 1,
|
||||
|
||||
@@ -4,9 +4,11 @@ import { If } from 'components';
|
||||
import { Skeleton } from 'components';
|
||||
import { useAppIntlContext } from 'components/AppIntlProvider';
|
||||
import TableContext from './TableContext';
|
||||
import { saveInvoke } from 'utils';
|
||||
import { saveInvoke, ignoreEventFromSelectors } from 'utils';
|
||||
import { isCellLoading } from './utils';
|
||||
|
||||
const ROW_CLICK_SELECTORS_INGORED = ['.expand-toggle', '.selection-checkbox'];
|
||||
|
||||
/**
|
||||
* Table cell.
|
||||
*/
|
||||
@@ -50,6 +52,9 @@ export default function TableCell({ cell, row, index }) {
|
||||
}
|
||||
// Handle cell click action.
|
||||
const handleCellClick = (event) => {
|
||||
if (ignoreEventFromSelectors(event, ROW_CLICK_SELECTORS_INGORED)) {
|
||||
return;
|
||||
}
|
||||
saveInvoke(onCellClick, cell, event);
|
||||
};
|
||||
|
||||
@@ -58,7 +63,7 @@ export default function TableCell({ cell, row, index }) {
|
||||
{...cell.getCellProps({
|
||||
className: classNames(cell.column.className, 'td', {
|
||||
'is-text-overview': cell.column.textOverview,
|
||||
'clickable': cell.column.clickable,
|
||||
clickable: cell.column.clickable,
|
||||
'align-right': cell.column.align === 'right',
|
||||
}),
|
||||
onClick: handleCellClick,
|
||||
@@ -83,11 +88,15 @@ export default function TableCell({ cell, row, index }) {
|
||||
// to build the toggle for expanding a row
|
||||
}
|
||||
<If condition={cell.row.canExpand && expandable && isExpandColumn}>
|
||||
<span {...getToggleRowExpandedProps({ className: 'expand-toggle' })}>
|
||||
<span
|
||||
{...getToggleRowExpandedProps({
|
||||
className: 'expand-toggle',
|
||||
})}
|
||||
style={{}}
|
||||
>
|
||||
<span
|
||||
className={classNames({
|
||||
'arrow-down': isExpanded,
|
||||
'arrow-right': !isExpanded,
|
||||
className={classNames('expand-arrow', {
|
||||
'is-expanded': isExpanded,
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Checkbox } from '@blueprintjs/core';
|
||||
|
||||
export default function TableIndeterminateCheckboxRow({ row }) {
|
||||
return (
|
||||
<div>
|
||||
<div class="selection-checkbox">
|
||||
<Checkbox {...row.getToggleRowSelectedProps()} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -14,12 +14,15 @@ export default function TablePagination() {
|
||||
pageCount,
|
||||
state: { pageIndex, pageSize },
|
||||
},
|
||||
props: { pagination, loading, onPaginationChange },
|
||||
props: { pagination, loading, onPaginationChange, hidePaginationNoPages },
|
||||
} = useContext(TableContext);
|
||||
|
||||
const triggerOnPaginationChange = useCallback((payload) => {
|
||||
saveInvoke(onPaginationChange, payload)
|
||||
}, [onPaginationChange]);
|
||||
const triggerOnPaginationChange = useCallback(
|
||||
(payload) => {
|
||||
saveInvoke(onPaginationChange, payload);
|
||||
},
|
||||
[onPaginationChange],
|
||||
);
|
||||
|
||||
// Handles the page changing.
|
||||
const handlePageChange = useCallback(
|
||||
@@ -45,8 +48,14 @@ export default function TablePagination() {
|
||||
[gotoPage, setPageSize, triggerOnPaginationChange],
|
||||
);
|
||||
|
||||
// Detarmines when display the pagination.
|
||||
const showPagination =
|
||||
pagination &&
|
||||
((hidePaginationNoPages && pageCount > 1) || !hidePaginationNoPages) &&
|
||||
!loading;
|
||||
|
||||
return (
|
||||
<If condition={pagination && !loading}>
|
||||
showPagination && (
|
||||
<Pagination
|
||||
currentPage={pageIndex + 1}
|
||||
total={pageSize * pageCount}
|
||||
@@ -54,6 +63,6 @@ export default function TablePagination() {
|
||||
onPageChange={handlePageChange}
|
||||
onPageSizeChange={handlePageSizeChange}
|
||||
/>
|
||||
</If>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useContext } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import clsx from 'classnames';
|
||||
import { ScrollSync } from 'react-scroll-sync';
|
||||
import TableContext from './TableContext';
|
||||
|
||||
@@ -9,12 +9,20 @@ import TableContext from './TableContext';
|
||||
export default function TableWrapper({ children }) {
|
||||
const {
|
||||
table: { getTableProps },
|
||||
props: { sticky, pagination, loading, expandable, virtualizedRows, className },
|
||||
props: {
|
||||
sticky,
|
||||
pagination,
|
||||
loading,
|
||||
expandable,
|
||||
virtualizedRows,
|
||||
className,
|
||||
size,
|
||||
},
|
||||
} = useContext(TableContext);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('bigcapital-datatable', className, {
|
||||
className={clsx('bigcapital-datatable', className, {
|
||||
'has-sticky': sticky,
|
||||
'has-pagination': pagination,
|
||||
'is-expandable': expandable,
|
||||
@@ -25,7 +33,9 @@ export default function TableWrapper({ children }) {
|
||||
<ScrollSync>
|
||||
<div
|
||||
{...getTableProps({ style: { minWidth: 'none' } })}
|
||||
className="table"
|
||||
className={clsx('table', {
|
||||
[`table-size--${size}`]: size,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,6 @@ import { compose } from 'utils';
|
||||
import withAuthentication from 'containers/Authentication/withAuthentication';
|
||||
import withOrganization from 'containers/Organization/withOrganization';
|
||||
|
||||
|
||||
function EnsureOrganizationIsReady({
|
||||
// #ownProps
|
||||
children,
|
||||
@@ -15,10 +14,10 @@ function EnsureOrganizationIsReady({
|
||||
// #withOrganizationByOrgId
|
||||
isOrganizationReady,
|
||||
}) {
|
||||
return (isOrganizationReady) ? children : (
|
||||
<Redirect
|
||||
to={{ pathname: redirectTo }}
|
||||
/>
|
||||
return isOrganizationReady ? (
|
||||
children
|
||||
) : (
|
||||
<Redirect to={{ pathname: redirectTo }} />
|
||||
);
|
||||
}
|
||||
|
||||
@@ -28,4 +27,4 @@ export default compose(
|
||||
organizationId: props.currentOrganizationId,
|
||||
})),
|
||||
withOrganization(({ isOrganizationReady }) => ({ isOrganizationReady })),
|
||||
)(EnsureOrganizationIsReady);
|
||||
)(EnsureOrganizationIsReady);
|
||||
|
||||
@@ -86,7 +86,7 @@ function Pagination({
|
||||
currentPage,
|
||||
total,
|
||||
size,
|
||||
pageSizesOptions = [5, 12, 20, 30, 50, 75, 100, 150],
|
||||
pageSizesOptions = [20, 30, 50, 75, 100, 150],
|
||||
onPageChange,
|
||||
onPageSizeChange,
|
||||
}) {
|
||||
|
||||
@@ -141,7 +141,6 @@ function UniversalSearchBar({ isOpen, onSearchTypeChange, ...listProps }) {
|
||||
{...handlers}
|
||||
>
|
||||
<InputGroup
|
||||
autoFocus={true}
|
||||
large={true}
|
||||
leftIcon={<Icon icon={'universal-search'} iconSize={20} />}
|
||||
placeholder={intl.get('universal_search.placeholder')}
|
||||
|
||||
@@ -58,6 +58,7 @@ import AccountsSuggestField from './AccountsSuggestField';
|
||||
import MaterialProgressBar from './MaterialProgressBar';
|
||||
import { MoneyFieldCell } from './DataTableCells';
|
||||
import Card from './Card';
|
||||
import AvaterCell from './AvaterCell';
|
||||
|
||||
import { ItemsMultiSelect } from './Items';
|
||||
|
||||
@@ -71,13 +72,13 @@ export * from './PdfPreview';
|
||||
export * from './Details';
|
||||
export * from './Drawer/DrawerInsider';
|
||||
export * from './Drawer/DrawerMainTabs';
|
||||
export * from './TotalLines/index'
|
||||
export * from './TotalLines/index';
|
||||
export * from './Alert';
|
||||
export * from './Subscriptions';
|
||||
export * from './Dashboard';
|
||||
export * from './Drawer';
|
||||
export * from './Forms';
|
||||
export * from './MultiSelectTaggable'
|
||||
export * from './MultiSelectTaggable';
|
||||
export * from './Utils/FormatNumber';
|
||||
export * from './Utils/FormatDate';
|
||||
|
||||
@@ -150,4 +151,5 @@ export {
|
||||
MoneyFieldCell,
|
||||
ItemsMultiSelect,
|
||||
Card,
|
||||
AvaterCell,
|
||||
};
|
||||
|
||||
@@ -12,6 +12,7 @@ import { useHistory } from 'react-router-dom';
|
||||
import {
|
||||
AdvancedFilterPopover,
|
||||
DashboardFilterButton,
|
||||
DashboardRowsHeightButton,
|
||||
FormattedMessage as T,
|
||||
} from 'components';
|
||||
|
||||
@@ -22,6 +23,8 @@ import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withManualJournalsActions from './withManualJournalsActions';
|
||||
import withManualJournals from './withManualJournals';
|
||||
import withSettingsActions from '../../Settings/withSettingsActions';
|
||||
import withSettings from '../../Settings/withSettings';
|
||||
|
||||
import { If, DashboardActionViewsList } from 'components';
|
||||
|
||||
@@ -36,6 +39,12 @@ function ManualJournalActionsBar({
|
||||
|
||||
// #withManualJournals
|
||||
manualJournalsFilterConditions,
|
||||
|
||||
// #withSettings
|
||||
manualJournalsTableSize,
|
||||
|
||||
// #withSettingsActions
|
||||
addSetting,
|
||||
}) {
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
@@ -62,6 +71,11 @@ function ManualJournalActionsBar({
|
||||
refresh();
|
||||
};
|
||||
|
||||
// Handle table row size change.
|
||||
const handleTableRowSizeChange = (size) => {
|
||||
addSetting('manualJournals', 'tableSize', size);
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -119,6 +133,12 @@ function ManualJournalActionsBar({
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<DashboardRowsHeightButton
|
||||
initialValue={manualJournalsTableSize}
|
||||
onChange={handleTableRowSizeChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<Button
|
||||
@@ -134,7 +154,11 @@ function ManualJournalActionsBar({
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withManualJournalsActions,
|
||||
withSettingsActions,
|
||||
withManualJournals(({ manualJournalsTableState }) => ({
|
||||
manualJournalsFilterConditions: manualJournalsTableState.filterRoles,
|
||||
})),
|
||||
withSettings(({ manualJournalsSettings }) => ({
|
||||
manualJournalsTableSize: manualJournalsSettings?.tableSize,
|
||||
})),
|
||||
)(ManualJournalActionsBar);
|
||||
|
||||
@@ -13,6 +13,7 @@ import withManualJournals from './withManualJournals';
|
||||
import withManualJournalsActions from './withManualJournalsActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import withSettings from '../../Settings/withSettings';
|
||||
|
||||
import { useManualJournalsContext } from './ManualJournalsListProvider';
|
||||
import { useMemorizedColumnsWidths } from 'hooks';
|
||||
@@ -38,6 +39,9 @@ function ManualJournalsDataTable({
|
||||
|
||||
// #ownProps
|
||||
onSelectedRowsChange,
|
||||
|
||||
// #withSettings
|
||||
manualJournalsTableSize,
|
||||
}) {
|
||||
// Manual journals context.
|
||||
const {
|
||||
@@ -109,7 +113,6 @@ function ManualJournalsDataTable({
|
||||
data={manualJournals}
|
||||
manualSortBy={true}
|
||||
selectionColumn={true}
|
||||
expandable={true}
|
||||
sticky={true}
|
||||
loading={isManualJournalsLoading}
|
||||
headerLoading={isManualJournalsLoading}
|
||||
@@ -125,6 +128,7 @@ function ManualJournalsDataTable({
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
size={manualJournalsTableSize}
|
||||
payload={{
|
||||
onDelete: handleDeleteJournal,
|
||||
onPublish: handlePublishJournal,
|
||||
@@ -143,4 +147,7 @@ export default compose(
|
||||
})),
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
withSettings(({ manualJournalsSettings }) => ({
|
||||
manualJournalsTableSize: manualJournalsSettings?.tableSize,
|
||||
})),
|
||||
)(ManualJournalsDataTable);
|
||||
|
||||
@@ -37,6 +37,7 @@ export const useManualJournalsColumns = () => {
|
||||
className: 'journal_number',
|
||||
width: 100,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'journal_type',
|
||||
@@ -44,6 +45,7 @@ export const useManualJournalsColumns = () => {
|
||||
accessor: 'journal_type',
|
||||
width: 110,
|
||||
clickable: true,
|
||||
textOverview: true,
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
If,
|
||||
DashboardActionViewsList,
|
||||
DashboardFilterButton,
|
||||
DashboardRowsHeightButton,
|
||||
} from 'components';
|
||||
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
@@ -27,7 +28,8 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withAccounts from 'containers/Accounts/withAccounts';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withAccountsTableActions from './withAccountsTableActions';
|
||||
|
||||
import withSettings from '../Settings/withSettings';
|
||||
import withSettingsActions from '../Settings/withSettingsActions';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
@@ -50,6 +52,12 @@ function AccountsActionsBar({
|
||||
|
||||
// #ownProps
|
||||
onFilterChanged,
|
||||
|
||||
// #withSettings
|
||||
accountsTableSize,
|
||||
|
||||
// #withSettingsActions
|
||||
addSetting,
|
||||
}) {
|
||||
const { resourceViews, fields } = useAccountsChartContext();
|
||||
|
||||
@@ -93,6 +101,10 @@ function AccountsActionsBar({
|
||||
refresh();
|
||||
};
|
||||
|
||||
// Handle table row size change.
|
||||
const handleTableRowSizeChange = (size) => {
|
||||
addSetting('accounts', 'tableSize', size);
|
||||
};
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -165,6 +177,12 @@ function AccountsActionsBar({
|
||||
icon={<Icon icon="file-import-16" iconSize={16} />}
|
||||
text={<T id={'import'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<DashboardRowsHeightButton
|
||||
initialValue={accountsTableSize}
|
||||
onChange={handleTableRowSizeChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<Switch
|
||||
labelElement={<T id={'inactive'} />}
|
||||
defaultChecked={accountsInactiveMode}
|
||||
@@ -185,10 +203,14 @@ function AccountsActionsBar({
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withAlertActions,
|
||||
withSettingsActions,
|
||||
withAccounts(({ accountsSelectedRows, accountsTableState }) => ({
|
||||
accountsSelectedRows,
|
||||
accountsInactiveMode: accountsTableState.inactiveMode,
|
||||
accountsFilterConditions: accountsTableState.filterRoles,
|
||||
})),
|
||||
withSettings(({ accountsSettings }) => ({
|
||||
accountsTableSize: accountsSettings.tableSize,
|
||||
})),
|
||||
withAccountsTableActions,
|
||||
)(AccountsActionsBar);
|
||||
|
||||
@@ -10,6 +10,7 @@ import { TABLES } from 'common/tables';
|
||||
import TableVirtualizedListRows from 'components/Datatable/TableVirtualizedRows';
|
||||
import TableSkeletonRows from 'components/Datatable/TableSkeletonRows';
|
||||
import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||
import withSettings from '../Settings/withSettings';
|
||||
|
||||
import { useAccountsChartContext } from './AccountsChartProvider';
|
||||
import { useMemorizedColumnsWidths } from '../../hooks';
|
||||
@@ -30,6 +31,9 @@ function AccountsDataTable({
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
|
||||
// #withSettings
|
||||
accountsTableSize,
|
||||
}) {
|
||||
const { isAccountsLoading, isAccountsFetching, accounts } =
|
||||
useAccountsChartContext();
|
||||
@@ -102,11 +106,12 @@ function AccountsDataTable({
|
||||
TableHeaderSkeletonRenderer={TableSkeletonHeader}
|
||||
ContextMenu={ActionsMenu}
|
||||
// #TableVirtualizedListRows props.
|
||||
vListrowHeight={42}
|
||||
vListrowHeight={accountsTableSize == 'small' ? 40 : 42}
|
||||
vListOverscanRowCount={0}
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
size={accountsTableSize}
|
||||
payload={{
|
||||
onEdit: handleEditAccount,
|
||||
onDelete: handleDeleteAccount,
|
||||
@@ -123,4 +128,7 @@ export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
withDialogActions,
|
||||
withSettings(({ accountsSettings }) => ({
|
||||
accountsTableSize: accountsSettings.tableSize,
|
||||
})),
|
||||
)(AccountsDataTable);
|
||||
|
||||
@@ -35,7 +35,7 @@ function BillTransactionDeleteAlert({
|
||||
deleteLandedCostMutate(BillId)
|
||||
.then(() => {
|
||||
AppToaster.show({
|
||||
message: intl.get('bill.action.delete_successfully.landed_cost'),
|
||||
message: intl.get('landed_cost.action.delete.success_message'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeAlert(name);
|
||||
@@ -56,7 +56,11 @@ function BillTransactionDeleteAlert({
|
||||
onConfirm={handleConfirmLandedCostDelete}
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ function CustomerBulkDeleteAlert({
|
||||
closeAlert(name);
|
||||
};
|
||||
|
||||
console.log(customersIds, 'EE');
|
||||
|
||||
|
||||
// Handle confirm customers bulk delete.
|
||||
const handleConfirmBulkDelete = useCallback(() => {
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import React, { useCallback } from 'react';
|
||||
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 { AppToaster } from 'components';
|
||||
import { transformErrors } from 'containers/Customers/utils';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { useDeleteCustomer } from 'hooks/query';
|
||||
import { compose } from 'utils';
|
||||
|
||||
|
||||
/**
|
||||
* Customer delete alert.
|
||||
*/
|
||||
@@ -24,12 +24,11 @@ function CustomerDeleteAlert({
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
|
||||
const {
|
||||
mutateAsync: deleteCustomerMutate,
|
||||
isLoading
|
||||
} = useDeleteCustomer();
|
||||
const { mutateAsync: deleteCustomerMutate, isLoading } = useDeleteCustomer();
|
||||
|
||||
// handle cancel delete alert.
|
||||
const handleCancelDeleteAlert = () => {
|
||||
@@ -44,10 +43,17 @@ function CustomerDeleteAlert({
|
||||
message: intl.get('the_customer_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('customer-details-drawer');
|
||||
})
|
||||
.catch(({ response: { data: { errors } } }) => {
|
||||
transformErrors(errors);
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
response: {
|
||||
data: { errors },
|
||||
},
|
||||
}) => {
|
||||
transformErrors(errors);
|
||||
},
|
||||
)
|
||||
.finally(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
@@ -76,4 +82,5 @@ function CustomerDeleteAlert({
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(CustomerDeleteAlert);
|
||||
|
||||
@@ -46,7 +46,24 @@ function EstimateDeleteAlert({
|
||||
});
|
||||
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(() => {
|
||||
closeAlert(name);
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback } from 'react';
|
||||
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 { AppToaster } from 'components';
|
||||
@@ -9,6 +9,7 @@ import { useDeleteVendor } from 'hooks/query';
|
||||
|
||||
import withAlertStoreConnect from 'containers/Alert/withAlertStoreConnect';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
@@ -24,8 +25,10 @@ function VendorDeleteAlert({
|
||||
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
|
||||
// #withDrawerActions
|
||||
closeDrawer,
|
||||
}) {
|
||||
|
||||
const { mutateAsync: deleteVendorMutate, isLoading } = useDeleteVendor();
|
||||
|
||||
// Handle cancel delete the vendor.
|
||||
@@ -41,6 +44,7 @@ function VendorDeleteAlert({
|
||||
message: intl.get('the_vendor_has_been_deleted_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
closeDrawer('vendor-details-drawer');
|
||||
})
|
||||
.catch(
|
||||
({
|
||||
@@ -79,4 +83,5 @@ function VendorDeleteAlert({
|
||||
export default compose(
|
||||
withAlertStoreConnect(),
|
||||
withAlertActions,
|
||||
withDrawerActions,
|
||||
)(VendorDeleteAlert);
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
@@ -8,16 +8,17 @@ import {
|
||||
Switch,
|
||||
Alignment,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import {
|
||||
If,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
DashboardActionViewsList,
|
||||
AdvancedFilterPopover,
|
||||
DashboardFilterButton,
|
||||
DashboardRowsHeightButton,
|
||||
} from 'components';
|
||||
|
||||
import { useCustomersListContext } from './CustomersListProvider';
|
||||
@@ -26,6 +27,8 @@ import { useRefreshCustomers } from 'hooks/query/customers';
|
||||
import withCustomers from './withCustomers';
|
||||
import withCustomersActions from './withCustomersActions';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withSettingsActions from '../../Settings/withSettingsActions';
|
||||
import withSettings from '../../Settings/withSettings';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
@@ -43,6 +46,12 @@ function CustomerActionsBar({
|
||||
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #withSettings
|
||||
customersTableSize,
|
||||
|
||||
// #withSettingsActions
|
||||
addSetting,
|
||||
}) {
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
@@ -74,7 +83,14 @@ function CustomerActionsBar({
|
||||
};
|
||||
|
||||
// Handle click a refresh customers
|
||||
const handleRefreshBtnClick = () => { refresh(); };
|
||||
const handleRefreshBtnClick = () => {
|
||||
refresh();
|
||||
};
|
||||
|
||||
// Handle table row size change.
|
||||
const handleTableRowSizeChange = (size) => {
|
||||
addSetting('customers', 'tableSize', size);
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
@@ -130,6 +146,12 @@ function CustomerActionsBar({
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<DashboardRowsHeightButton
|
||||
initialValue={customersTableSize}
|
||||
onChange={handleTableRowSizeChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<Switch
|
||||
labelElement={<T id={'inactive'} />}
|
||||
defaultChecked={accountsInactiveMode}
|
||||
@@ -149,10 +171,14 @@ function CustomerActionsBar({
|
||||
|
||||
export default compose(
|
||||
withCustomersActions,
|
||||
withSettingsActions,
|
||||
withCustomers(({ customersSelectedRows, customersTableState }) => ({
|
||||
customersSelectedRows,
|
||||
accountsInactiveMode: customersTableState.inactiveMode,
|
||||
customersFilterConditions: customersTableState.filterRoles,
|
||||
})),
|
||||
withSettings(({ customersSettings }) => ({
|
||||
customersTableSize: customersSettings?.tableSize,
|
||||
})),
|
||||
withAlertActions,
|
||||
)(CustomerActionsBar);
|
||||
|
||||
@@ -14,6 +14,7 @@ import withCustomersActions from './withCustomersActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import withSettings from '../../Settings/withSettings';
|
||||
|
||||
import { useCustomersListContext } from './CustomersListProvider';
|
||||
import { useMemorizedColumnsWidths } from 'hooks';
|
||||
@@ -38,6 +39,9 @@ function CustomersTable({
|
||||
|
||||
// #withDialogActions
|
||||
openDialog,
|
||||
|
||||
// #withSettings
|
||||
customersTableSize,
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
@@ -135,6 +139,7 @@ function CustomersTable({
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
size={customersTableSize}
|
||||
payload={{
|
||||
onDelete: handleCustomerDelete,
|
||||
onEdit: handleCustomerEdit,
|
||||
@@ -155,4 +160,7 @@ export default compose(
|
||||
withCustomersActions,
|
||||
withDrawerActions,
|
||||
withCustomers(({ customersTableState }) => ({ customersTableState })),
|
||||
withSettings(({ customersSettings }) => ({
|
||||
customersTableSize: customersSettings?.tableSize,
|
||||
})),
|
||||
)(CustomersTable);
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import {
|
||||
Menu,
|
||||
MenuItem,
|
||||
MenuDivider,
|
||||
Intent,
|
||||
} from '@blueprintjs/core';
|
||||
import { Menu, MenuItem, MenuDivider, Intent } from '@blueprintjs/core';
|
||||
import clsx from 'classnames';
|
||||
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { CLASSES } from '../../../common/classes';
|
||||
import { Icon, Money, If } from 'components';
|
||||
import { } from 'utils';
|
||||
import { safeCallback, firstLettersArgs } from 'utils';
|
||||
import { Icon, Money, If, AvaterCell } from 'components';
|
||||
|
||||
import { safeCallback } from 'utils';
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -99,7 +87,7 @@ export function useCustomersTableColumns() {
|
||||
{
|
||||
id: 'avatar',
|
||||
Header: '',
|
||||
accessor: AvatarCell,
|
||||
Cell: AvaterCell,
|
||||
className: 'avatar',
|
||||
width: 45,
|
||||
disableResizing: true,
|
||||
|
||||
@@ -11,8 +11,8 @@ export default (mapState) => {
|
||||
sidebarExpended: state.dashboard.sidebarExpended,
|
||||
preferencesPageTitle: state.dashboard.preferencesPageTitle,
|
||||
dashboardBackLink: state.dashboard.backLink,
|
||||
appIsLoading: state.dashboard.appIsLoading,
|
||||
appIntlIsLoading: state.dashboard.appIntlIsLoading
|
||||
splashScreenLoading: state.dashboard.splashScreenLoading > 0,
|
||||
splashScreenCompleted: state.dashboard.splashScreenLoading === 0,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
|
||||
@@ -2,9 +2,8 @@ import { connect } from 'react-redux';
|
||||
import t from 'store/types';
|
||||
import {
|
||||
toggleExpendSidebar,
|
||||
appIsLoading,
|
||||
appIntlIsLoading
|
||||
} from 'store/dashboard/dashboard.actions';
|
||||
import { splashStartLoading, splashStopLoading } from '../../store/dashboard/dashboard.actions';
|
||||
|
||||
const mapActionsToProps = (dispatch) => ({
|
||||
changePageTitle: (pageTitle) =>
|
||||
@@ -50,15 +49,17 @@ const mapActionsToProps = (dispatch) => ({
|
||||
dispatch({
|
||||
type: 'CHANGE_PREFERENCES_PAGE_TITLE',
|
||||
pageTitle,
|
||||
}),
|
||||
}),
|
||||
|
||||
setDashboardBackLink: (backLink) =>
|
||||
dispatch({
|
||||
type: t.SET_DASHBOARD_BACK_LINK,
|
||||
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);
|
||||
|
||||
@@ -21,7 +21,7 @@ export const transformErrors = (errors, { setFieldError }) => {
|
||||
intl.get('payment_number_is_not_unique'),
|
||||
);
|
||||
}
|
||||
if (getError('INVALID_PAYMENT_AMOUNT')) {
|
||||
if (getError('INVALID_BILL_PAYMENT_AMOUNT')) {
|
||||
setFieldError(
|
||||
'payment_amount',
|
||||
intl.get('the_payment_amount_bigger_than_invoice_due_amount'),
|
||||
|
||||
@@ -16,7 +16,7 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
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';
|
||||
|
||||
@@ -32,7 +32,7 @@ function BillDetailActionsBar({
|
||||
}) {
|
||||
const history = useHistory();
|
||||
|
||||
const { billId } = useBillDrawerContext();
|
||||
const { billId, bill } = useBillDrawerContext();
|
||||
|
||||
// Handle edit bill.
|
||||
const onEditBill = () => {
|
||||
@@ -45,6 +45,11 @@ function BillDetailActionsBar({
|
||||
openAlert('bill-delete', { billId });
|
||||
};
|
||||
|
||||
// Handle quick bill payment .
|
||||
const handleQuickBillPayment = () => {
|
||||
openDialog('quick-payment-made', { billId });
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -55,6 +60,15 @@ function BillDetailActionsBar({
|
||||
onClick={safeCallback(onEditBill)}
|
||||
/>
|
||||
<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
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon={'trash-16'} iconSize={16} />}
|
||||
|
||||
@@ -61,8 +61,8 @@ function CustomerDetailsActionsBar({
|
||||
|
||||
const handleDeleteCustomer = () => {
|
||||
openAlert(`customer-delete`, { contactId: customerId });
|
||||
closeDrawer('customer-details-drawer');
|
||||
};
|
||||
|
||||
const handleEditContact = () => {
|
||||
history.push(`/customers/${customerId}/edit`);
|
||||
closeDrawer('customer-details-drawer');
|
||||
@@ -108,7 +108,7 @@ function CustomerDetailsActionsBar({
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={intl.get('edit_contact', { name: contact?.contact_service })}
|
||||
text={intl.get('customer.drawer.action.edit')}
|
||||
onClick={handleEditContact}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
@@ -19,12 +19,7 @@ function InventoryAdjustmentDetailDrawer({
|
||||
payload: { inventoryId },
|
||||
}) {
|
||||
return (
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
name={name}
|
||||
style={{ minWidth: '700px', maxWidth: '900px' }}
|
||||
size={'65%'}
|
||||
>
|
||||
<Drawer isOpen={isOpen} name={name} size={'750px'}>
|
||||
<DrawerSuspense>
|
||||
<InventoryAdjustmentDrawerContent inventoryId={inventoryId} />
|
||||
</DrawerSuspense>
|
||||
|
||||
@@ -16,7 +16,7 @@ import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
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';
|
||||
|
||||
@@ -36,7 +36,7 @@ function InvoiceDetailActionsBar({
|
||||
const history = useHistory();
|
||||
|
||||
// Invoice detail drawer context.
|
||||
const { invoiceId } = useInvoiceDetailDrawerContext();
|
||||
const { invoiceId, invoice } = useInvoiceDetailDrawerContext();
|
||||
|
||||
// Handle edit sale invoice.
|
||||
const handleEditInvoice = () => {
|
||||
@@ -54,6 +54,11 @@ function InvoiceDetailActionsBar({
|
||||
openDialog('invoice-pdf-preview', { invoiceId });
|
||||
};
|
||||
|
||||
// Handle quick payment invoice.
|
||||
const handleQuickPaymentInvoice = () => {
|
||||
openDialog('quick-payment-receive', { invoiceId });
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -64,6 +69,15 @@ function InvoiceDetailActionsBar({
|
||||
onClick={handleEditInvoice}
|
||||
/>
|
||||
<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
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="print-16" />}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { useManualJournalDrawerContext } from './ManualJournalDrawerProvider';
|
||||
|
||||
import { FormatNumber } from '../../../components';
|
||||
import { T, FormatNumber } from '../../../components';
|
||||
|
||||
/**
|
||||
* Manual journal readonly details footer.
|
||||
@@ -15,7 +15,9 @@ export default function ManualJournalDrawerFooter() {
|
||||
<div className="journal-drawer__content-footer">
|
||||
<div class="total-lines">
|
||||
<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">
|
||||
<FormatNumber value={amount} />
|
||||
</div>
|
||||
@@ -24,7 +26,9 @@ export default function ManualJournalDrawerFooter() {
|
||||
</div>
|
||||
</div>
|
||||
<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="credit">{formatted_amount}</div>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,10 @@ export default function ManualJournalDrawerHeader() {
|
||||
</DetailsMenu>
|
||||
|
||||
<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>
|
||||
);
|
||||
|
||||
@@ -34,7 +34,7 @@ function PaymentMadeDetailProvider({ paymentMadeId, ...props }) {
|
||||
} = useTransactionsByReference(
|
||||
{
|
||||
reference_id: paymentMadeId,
|
||||
reference_type: 'paymentMade',
|
||||
reference_type: 'BillPayment',
|
||||
},
|
||||
{ enabled: !!paymentMadeId },
|
||||
);
|
||||
|
||||
@@ -46,7 +46,6 @@ function VendorDetailsActionsBar({
|
||||
// Handle delete vendor.
|
||||
const onDeleteContact = () => {
|
||||
openAlert(`vendor-delete`, { contactId: vendorId });
|
||||
closeDrawer('vendor-details-drawer');
|
||||
};
|
||||
|
||||
const handleNewInvoiceClick = () => {
|
||||
@@ -91,7 +90,7 @@ function VendorDetailsActionsBar({
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="pen-18" />}
|
||||
text={<T id={'vendor.drawer.action.edit_vendor'} />}
|
||||
text={<T id={'vendor.drawer.action.edit'} />}
|
||||
onClick={safeCallback(onEditContact)}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
|
||||
@@ -6,7 +6,6 @@ import { Formik, Form } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import * as R from 'ramda';
|
||||
|
||||
import ExpenseFormHeader from './ExpenseFormHeader';
|
||||
import ExpenseFormBody from './ExpenseFormBody';
|
||||
@@ -25,8 +24,13 @@ import {
|
||||
CreateExpenseFormSchema,
|
||||
EditExpenseFormSchema,
|
||||
} from './ExpenseForm.schema';
|
||||
import { transformErrors, defaultExpense, transformToEditForm } from './utils';
|
||||
import { compose, orderingLinesIndexes } from 'utils';
|
||||
import {
|
||||
transformErrors,
|
||||
defaultExpense,
|
||||
transformToEditForm,
|
||||
transformFormValuesToRequest,
|
||||
} from './utils';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
* Expense form.
|
||||
@@ -79,15 +83,10 @@ function ExpenseForm({
|
||||
});
|
||||
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 = {
|
||||
...values,
|
||||
...transformFormValuesToRequest(values),
|
||||
publish: submitPayload.publish,
|
||||
categories: R.compose(orderingLinesIndexes)(categories),
|
||||
};
|
||||
// Handle request success.
|
||||
const handleSuccess = (response) => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { AppToaster } from 'components';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import moment from 'moment';
|
||||
import intl from 'react-intl-universal';
|
||||
import * as R from 'ramda';
|
||||
@@ -6,11 +7,14 @@ import {
|
||||
defaultFastFieldShouldUpdate,
|
||||
transformToForm,
|
||||
repeatValue,
|
||||
ensureEntriesHasEmptyLine
|
||||
ensureEntriesHasEmptyLine,
|
||||
orderingLinesIndexes
|
||||
} from 'utils';
|
||||
|
||||
const ERROR = {
|
||||
EXPENSE_ALREADY_PUBLISHED: 'EXPENSE.ALREADY.PUBLISHED',
|
||||
ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED:
|
||||
'ENTRIES_ALLOCATED_COST_COULD_NOT_DELETED',
|
||||
};
|
||||
|
||||
// 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;
|
||||
@@ -93,3 +105,25 @@ export const accountsFieldShouldUpdate = (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),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9,16 +9,17 @@ import {
|
||||
Alignment,
|
||||
} from '@blueprintjs/core';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
|
||||
import {
|
||||
If,
|
||||
DashboardRowsHeightButton,
|
||||
DashboardActionViewsList,
|
||||
DashboardFilterButton,
|
||||
AdvancedFilterPopover,
|
||||
FormattedMessage as T,
|
||||
} from 'components';
|
||||
|
||||
import { useRefreshExpenses } from 'hooks/query/expenses';
|
||||
@@ -27,6 +28,9 @@ import { useExpensesListContext } from './ExpensesListProvider';
|
||||
import withExpensesActions from './withExpensesActions';
|
||||
import withExpenses from './withExpenses';
|
||||
|
||||
import withSettingsActions from '../../Settings/withSettingsActions';
|
||||
import withSettings from '../../Settings/withSettings';
|
||||
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
@@ -38,6 +42,12 @@ function ExpensesActionsBar({
|
||||
|
||||
// #withExpenses
|
||||
expensesFilterConditions,
|
||||
|
||||
// #withSettings
|
||||
expensesTableSize,
|
||||
|
||||
// #withSettingsActions
|
||||
addSetting,
|
||||
}) {
|
||||
// History context.
|
||||
const history = useHistory();
|
||||
@@ -67,6 +77,11 @@ function ExpensesActionsBar({
|
||||
const handleRefreshBtnClick = () => {
|
||||
refresh();
|
||||
};
|
||||
|
||||
// Handle table row size change.
|
||||
const handleTableRowSizeChange = (size) => {
|
||||
addSetting('expenses', 'tableSize', size);
|
||||
};
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
<NavbarGroup>
|
||||
@@ -124,6 +139,12 @@ function ExpensesActionsBar({
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<DashboardRowsHeightButton
|
||||
initialValue={expensesTableSize}
|
||||
onChange={handleTableRowSizeChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</NavbarGroup>
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<Button
|
||||
@@ -139,7 +160,11 @@ function ExpensesActionsBar({
|
||||
export default compose(
|
||||
withDialogActions,
|
||||
withExpensesActions,
|
||||
withSettingsActions,
|
||||
withExpenses(({ expensesTableState }) => ({
|
||||
expensesFilterConditions: expensesTableState.filterRoles,
|
||||
})),
|
||||
withSettings(({ expenseSettings }) => ({
|
||||
expensesTableSize: expenseSettings?.tableSize,
|
||||
})),
|
||||
)(ExpensesActionsBar);
|
||||
|
||||
@@ -16,6 +16,7 @@ import withDashboardActions from 'containers/Dashboard/withDashboardActions';
|
||||
import withExpensesActions from './withExpensesActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import withSettings from '../../Settings/withSettings';
|
||||
|
||||
import { ActionsMenu, useExpensesTableColumns } from './components';
|
||||
|
||||
@@ -31,6 +32,9 @@ function ExpensesDataTable({
|
||||
|
||||
// #withAlertsActions
|
||||
openAlert,
|
||||
|
||||
// #withSettings
|
||||
expensesTableSize,
|
||||
}) {
|
||||
// Expenses list context.
|
||||
const {
|
||||
@@ -119,6 +123,7 @@ function ExpensesDataTable({
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
size={expensesTableSize}
|
||||
payload={{
|
||||
onPublish: handlePublishExpense,
|
||||
onDelete: handleDeleteExpense,
|
||||
@@ -135,4 +140,7 @@ export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
withExpensesActions,
|
||||
withSettings(({ expenseSettings }) => ({
|
||||
expensesTableSize: expenseSettings?.tableSize,
|
||||
})),
|
||||
)(ExpensesDataTable);
|
||||
|
||||
@@ -31,7 +31,7 @@ function BalanceSheet({
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'cash',
|
||||
displayColumnsType: 'total',
|
||||
accountsFilter: 'without-zero-balance',
|
||||
filterByOption: 'without-zero-balance',
|
||||
});
|
||||
|
||||
// Handle re-fetch balance sheet after filter change.
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
|
||||
/**
|
||||
* Balance sheet header - General panal.
|
||||
@@ -12,7 +12,7 @@ export default function BalanceSheetHeaderGeneralTab({}) {
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<SelectDisplayColumnsBy />
|
||||
<FinancialAccountsFilter
|
||||
<FinancialStatementsFilter
|
||||
initialSelectedItem={'all-accounts'}
|
||||
/>
|
||||
<RadiosAccountingBasis key={'basis'} />
|
||||
|
||||
@@ -5,7 +5,7 @@ import moment from 'moment';
|
||||
export const getBalanceSheetHeaderDefaultValues = () => {
|
||||
return {
|
||||
basic: 'cash',
|
||||
accountsFilter: 'without-zero-balance',
|
||||
filterByOption: 'without-zero-balance',
|
||||
displayColumnsType: 'total',
|
||||
fromDate: moment().toDate(),
|
||||
toDate: moment().toDate(),
|
||||
@@ -20,6 +20,6 @@ export const getBalanceSheetHeaderValidationSchema = () =>
|
||||
.min(Yup.ref('fromDate'))
|
||||
.required()
|
||||
.label(intl.get('toDate')),
|
||||
accountsFilter: Yup.string(),
|
||||
filterByOption: Yup.string(),
|
||||
displayColumnsType: Yup.string(),
|
||||
});
|
||||
|
||||
@@ -34,6 +34,7 @@ function CashFlowStatement({
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'cash',
|
||||
displayColumnsType: 'total',
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle refetch cash flow after filter change.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
||||
|
||||
@@ -13,7 +13,7 @@ export default function CashFlowStatementHeaderGeneralPanel() {
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<SelectDisplayColumnsBy />
|
||||
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} />
|
||||
<FinancialStatementsFilter initialSelectedItem={'with-transactions'} />
|
||||
<RadiosAccountingBasis key={'basis'} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -29,6 +29,7 @@ function CustomersBalanceSummary({
|
||||
}) {
|
||||
const [filter, setFilter] = useState({
|
||||
asDate: moment().endOf('day').format('YYYY-MM-DD'),
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle re-fetch customers balance summary after filter change.
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Classes, FormGroup, Position, Checkbox } from '@blueprintjs/core';
|
||||
import { ContactsMultiSelect, FormattedMessage as T } from 'components';
|
||||
import classNames from 'classnames';
|
||||
import { Row, Col, FieldHint } from 'components';
|
||||
import { filterCustomersOptions } from '../constants';
|
||||
|
||||
import {
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
@@ -12,6 +14,7 @@ import {
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
import { useCustomersBalanceSummaryGeneralContext } from './CustomersBalanceSummaryGeneralProvider';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
|
||||
/**
|
||||
* Customers balance header - General panel - Content
|
||||
@@ -65,6 +68,12 @@ export default function CustomersBalanceSummaryGeneralPanelContent() {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FinancialStatementsFilter
|
||||
items={filterCustomersOptions}
|
||||
label={<T id={'customers.label_filter_customers'} />}
|
||||
initialSelectedItem={'with-transactions'}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<Field name={'customersIds'}>
|
||||
@@ -80,7 +89,7 @@ export default function CustomersBalanceSummaryGeneralPanelContent() {
|
||||
<ContactsMultiSelect
|
||||
items={customers}
|
||||
onItemSelect={(contacts) => {
|
||||
const customersIds = contacts.map(contact => contact.id);
|
||||
const customersIds = contacts.map((contact) => contact.id);
|
||||
setFieldValue('customersIds', customersIds);
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -26,7 +26,6 @@ function CustomersBalanceSummaryHeader({
|
||||
// #withCustomersBalanceSummaryActions
|
||||
toggleCustomerBalanceFilterDrawer,
|
||||
}) {
|
||||
|
||||
// validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
asDate: Yup.date().required().label('asDate'),
|
||||
@@ -34,15 +33,20 @@ function CustomersBalanceSummaryHeader({
|
||||
|
||||
// Default form values.
|
||||
const defaultValues = {
|
||||
...pageFilter,
|
||||
asDate: moment().toDate(),
|
||||
customersIds: [],
|
||||
};
|
||||
|
||||
// Filter form initial values.
|
||||
const initialValues = transformToForm({
|
||||
...pageFilter,
|
||||
asDate: moment(pageFilter.asDate).toDate(),
|
||||
}, defaultValues);
|
||||
const initialValues = transformToForm(
|
||||
{
|
||||
...defaultValues,
|
||||
...pageFilter,
|
||||
asDate: moment(pageFilter.asDate).toDate(),
|
||||
},
|
||||
defaultValues,
|
||||
);
|
||||
|
||||
// handle form submit.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
|
||||
@@ -30,6 +30,7 @@ function CustomersTransactions({
|
||||
const [filter, setFilter] = useState({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
const handleFilterSubmit = (filter) => {
|
||||
|
||||
@@ -30,22 +30,25 @@ function CustomersTransactionsHeader({
|
||||
}) {
|
||||
// Default form values.
|
||||
const defaultValues = {
|
||||
...pageFilter,
|
||||
fromDate: moment().toDate(),
|
||||
toDate: moment().toDate(),
|
||||
customersIds: [],
|
||||
};
|
||||
// Initial form values.
|
||||
const initialValues = transformToForm({
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
}, defaultValues);
|
||||
const initialValues = transformToForm(
|
||||
{
|
||||
...defaultValues,
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
},
|
||||
defaultValues,
|
||||
);
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
fromDate: Yup.date()
|
||||
.required()
|
||||
.label(intl.get('fromDate')),
|
||||
fromDate: Yup.date().required().label(intl.get('fromDate')),
|
||||
toDate: Yup.date()
|
||||
.min(Yup.ref('fromDate'))
|
||||
.required()
|
||||
@@ -59,7 +62,9 @@ function CustomersTransactionsHeader({
|
||||
setSubmitting(false);
|
||||
};
|
||||
// Handle drawer close action.
|
||||
const handleDrawerClose = () => { toggleFilterDrawer(false); };
|
||||
const handleDrawerClose = () => {
|
||||
toggleFilterDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader
|
||||
|
||||
@@ -3,12 +3,15 @@ import classNames from 'classnames';
|
||||
import { Field } from 'formik';
|
||||
import { Classes, FormGroup } from '@blueprintjs/core';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
ContactsMultiSelect,
|
||||
FormattedMessage as T,
|
||||
} from '../../../components';
|
||||
import { filterCustomersOptions } from '../constants';
|
||||
|
||||
import {
|
||||
CustomersTransactionsGeneralPanelProvider,
|
||||
useCustomersTransactionsGeneralPanelContext,
|
||||
@@ -34,7 +37,11 @@ function CustomersTransactionsHeaderGeneralPanelContent() {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
|
||||
<FinancialStatementsFilter
|
||||
items={filterCustomersOptions}
|
||||
label={<T id={'customers.label_filter_customers'} />}
|
||||
initialSelectedItem={'with-transactions'}
|
||||
/>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<Field name={'customersIds'}>
|
||||
|
||||
@@ -6,7 +6,8 @@ import { Row, Col, Hint } from 'components';
|
||||
import { momentFormatter, parseDateRangeQuery } from 'utils';
|
||||
import { DateInput } from '@blueprintjs/datetime';
|
||||
import intl from 'react-intl-universal';
|
||||
import { dateRangeOptions } from 'containers/FinancialStatements/common';
|
||||
import { dateRangeOptions } from './constants';
|
||||
|
||||
|
||||
/**
|
||||
* Financial statement - Date range select.
|
||||
|
||||
@@ -12,17 +12,21 @@ import { FastField } from 'formik';
|
||||
|
||||
import { CLASSES } from 'common/classes';
|
||||
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 = {
|
||||
flip: { boundariesElement: 'viewport', padding: 20 },
|
||||
offset: { offset: '0, 10' },
|
||||
preventOverflow: { boundariesElement: 'viewport', padding: 40 },
|
||||
};
|
||||
|
||||
const filterAccountRenderer = (item, { handleClick, modifiers, query }) => {
|
||||
const filterRenderer = (item, { handleClick, modifiers, query }) => {
|
||||
return (
|
||||
<Tooltip
|
||||
interactionKind={PopoverInteractionKind.HOVER}
|
||||
@@ -41,23 +45,23 @@ export default function FinancialAccountsFilter({ ...restProps }) {
|
||||
return (
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<FastField name={'accountsFilter'}>
|
||||
<FastField name={'filterByOption'}>
|
||||
{({ form: { setFieldValue }, field: { value } }) => (
|
||||
<FormGroup
|
||||
label={<T id={'filter_accounts'} />}
|
||||
label={label}
|
||||
className="form-group--select-list bp3-fill"
|
||||
inline={false}
|
||||
>
|
||||
<ListSelect
|
||||
items={filterAccountsOptions}
|
||||
itemRenderer={filterAccountRenderer}
|
||||
items={items}
|
||||
itemRenderer={filterRenderer}
|
||||
popoverProps={{ minimal: true }}
|
||||
filterable={false}
|
||||
selectedItem={value}
|
||||
selectedItemProp={'key'}
|
||||
textProp={'name'}
|
||||
onItemSelect={(item) => {
|
||||
setFieldValue('accountsFilter', item.key);
|
||||
setFieldValue('filterByOption', item.key);
|
||||
}}
|
||||
className={classNames(CLASSES.SELECT_LIST_FILL_POPOVER)}
|
||||
{...restProps}
|
||||
@@ -34,7 +34,7 @@ function GeneralLedger({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'accural',
|
||||
accountsFilter: 'with-transactions',
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle financial statement filter change.
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AccountsMultiSelect, Row, Col } from 'components';
|
||||
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
import { GLHeaderGeneralPanelProvider } from './GLHeaderGeneralPaneProvider';
|
||||
|
||||
import { filterAccountsOptions } from './common';
|
||||
@@ -33,9 +33,9 @@ function GLHeaderGeneralPaneContent() {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FinancialStatementDateRange />
|
||||
<FinancialAccountsFilter
|
||||
<FinancialStatementsFilter
|
||||
items={filterAccountsOptions}
|
||||
initialSelectedItem={'all-accounts'}
|
||||
initialSelectedItem={'with-transactions'}
|
||||
/>
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
|
||||
@@ -27,6 +27,7 @@ function InventoryValuation({
|
||||
}) {
|
||||
const [filter, setFilter] = useState({
|
||||
asDate: moment().endOf('day').format('YYYY-MM-DD'),
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle filter form submit.
|
||||
@@ -54,7 +55,6 @@ function InventoryValuation({
|
||||
[toggleInventoryValuationFilterDrawer],
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<InventoryValuationProvider query={filter}>
|
||||
<InventoryValuationActionsBar
|
||||
|
||||
@@ -33,12 +33,14 @@ function InventoryValuationHeader({
|
||||
|
||||
// Default values.
|
||||
const defaultValues = {
|
||||
...pageFilter,
|
||||
asDate: moment().toDate(),
|
||||
itemsIds: [],
|
||||
};
|
||||
// Initial values.
|
||||
const initialValues = transformToForm({
|
||||
...pageFilter,
|
||||
...defaultValues,
|
||||
asDate: moment(pageFilter.asDate).toDate(),
|
||||
}, defaultValues);
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
Col,
|
||||
FieldHint,
|
||||
} from '../../../components';
|
||||
import { filterInventoryValuationOptions } from '../constants';
|
||||
|
||||
import {
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
InventoryValuationGeneralPanelProvider,
|
||||
useInventoryValuationGeneralPanelContext,
|
||||
} from './InventoryValuationHeaderGeneralPanelProvider';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
|
||||
/**
|
||||
* Inventory valuation - Drawer Header - General panel.
|
||||
@@ -42,7 +45,7 @@ function InventoryValuationHeaderGeneralPanelContent() {
|
||||
return (
|
||||
<div>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<Col xs={4}>
|
||||
<FastField name={'asDate'}>
|
||||
{({ form, field: { value }, meta: { error } }) => (
|
||||
<FormGroup
|
||||
@@ -66,9 +69,14 @@ function InventoryValuationHeaderGeneralPanelContent() {
|
||||
</FastField>
|
||||
</Col>
|
||||
</Row>
|
||||
<FinancialStatementsFilter
|
||||
items={filterInventoryValuationOptions}
|
||||
label={<T id={'items.label_filter_items'} />}
|
||||
initialSelectedItem={'all-items'}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<Col xs={4}>
|
||||
<Field name={'itemsIds'}>
|
||||
{({ form: { setFieldValue } }) => (
|
||||
<FormGroup
|
||||
|
||||
@@ -14,10 +14,7 @@ import withCurrentOrganization from '../../Organization/withCurrentOrganization'
|
||||
|
||||
import 'style/pages/FinancialStatements/ProfitLossSheet.scss';
|
||||
import { ProfitLossSheetProvider } from './ProfitLossProvider';
|
||||
import {
|
||||
ProfitLossSheetLoadingBar,
|
||||
ProfitLossSheetAlerts
|
||||
} from './components';
|
||||
import { ProfitLossSheetLoadingBar, ProfitLossSheetAlerts } from './components';
|
||||
|
||||
/**
|
||||
* Profit/Loss financial statement sheet.
|
||||
@@ -34,7 +31,7 @@ function ProfitLossSheet({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
displayColumnsType: 'total',
|
||||
accountsFilter: 'all-accounts',
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle submit filter.
|
||||
|
||||
@@ -25,18 +25,14 @@ function ProfitLossHeader({
|
||||
// #withProfitLossActions
|
||||
toggleProfitLossFilterDrawer: toggleFilterDrawer,
|
||||
}) {
|
||||
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
fromDate: Yup.date()
|
||||
.required()
|
||||
.label(intl.get('from_date')),
|
||||
fromDate: Yup.date().required().label(intl.get('from_date')),
|
||||
toDate: Yup.date()
|
||||
.min(Yup.ref('fromDate'))
|
||||
.required()
|
||||
.label(intl.get('to_date')),
|
||||
accountsFilter: Yup.string(),
|
||||
filterByOption: Yup.string(),
|
||||
displayColumnsType: Yup.string(),
|
||||
});
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import React from 'react';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import SelectDisplayColumnsBy from '../SelectDisplayColumnsBy';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
|
||||
/**
|
||||
* Profit/Loss sheet - Drawer header - General panel.
|
||||
@@ -13,7 +13,7 @@ export default function ProfitLossSheetHeaderGeneralPane({}) {
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<SelectDisplayColumnsBy />
|
||||
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} />
|
||||
<FinancialStatementsFilter initialSelectedItem={'with-transactions'} />
|
||||
<RadiosAccountingBasis key={'basis'} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -27,6 +27,7 @@ function PurchasesByItems({
|
||||
const [filter, setFilter] = useState({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle filter form submit.
|
||||
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
} from '../../../components';
|
||||
import classNames from 'classnames';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
import { filterItemsOptions } from '../constants';
|
||||
|
||||
import {
|
||||
PurchasesByItemsGeneralPanelProvider,
|
||||
@@ -35,6 +37,11 @@ function PurchasesByItemsGeneralPanelContent() {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<FinancialStatementsFilter
|
||||
items={filterItemsOptions}
|
||||
label={<T id={'items.label_filter_items'} />}
|
||||
initialSelectedItem={'with-transactions'}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
|
||||
@@ -47,6 +47,7 @@ function PurchasesByItemsHeader({
|
||||
// Initial form values.
|
||||
const initialValues = transformToForm(
|
||||
{
|
||||
...defaultValues,
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { useEffect, useState, useCallback } from 'react';
|
||||
import moment from 'moment';
|
||||
|
||||
|
||||
import 'style/pages/FinancialStatements/SalesAndPurchasesSheet.scss';
|
||||
|
||||
import { SalesByItemProvider } from './SalesByItemProvider';
|
||||
@@ -30,6 +29,7 @@ function SalesByItems({
|
||||
const [filter, setFilter] = useState({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle filter form submit.
|
||||
|
||||
@@ -2,10 +2,11 @@ import React from 'react';
|
||||
import { FormGroup, Classes } from '@blueprintjs/core';
|
||||
import { Field } from 'formik';
|
||||
import classNames from 'classnames';
|
||||
import { get } from 'lodash';
|
||||
import { filterItemsOptions } from '../constants';
|
||||
|
||||
import { Row, Col, ItemsMultiSelect, FormattedMessage as T } from 'components';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
import {
|
||||
SalesByItemGeneralPanelProvider,
|
||||
useSalesByItemsGeneralPanelContext,
|
||||
@@ -32,6 +33,12 @@ function SalesByItemsHeaderGeneralPanelContent() {
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
|
||||
<FinancialStatementsFilter
|
||||
items={filterItemsOptions}
|
||||
label={<T id={'items.label_filter_items'} />}
|
||||
initialSelectedItem={'with-transactions'}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<Field name={'itemsIds'}>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { FormGroup } from '@blueprintjs/core';
|
||||
import { FastField } from 'formik';
|
||||
import { FormattedMessage as T } 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.
|
||||
|
||||
@@ -33,7 +33,7 @@ function TrialBalanceSheet({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
basis: 'accural',
|
||||
accountsFilter: 'all-accounts',
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle filter form submit.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import FinancialStatementDateRange from 'containers/FinancialStatements/FinancialStatementDateRange';
|
||||
import RadiosAccountingBasis from '../RadiosAccountingBasis';
|
||||
import FinancialAccountsFilter from '../FinancialAccountsFilter';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
|
||||
/**
|
||||
* Trial balance sheet - Drawer header - General panel.
|
||||
@@ -12,7 +12,7 @@ export default function TrialBalanceSheetHeaderGeneralPanel({
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
<FinancialAccountsFilter initialSelectedItem={'all-accounts'} />
|
||||
<FinancialStatementsFilter initialSelectedItem={'with-transactions'} />
|
||||
<RadiosAccountingBasis />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -30,6 +30,7 @@ function VendorsBalanceSummary({
|
||||
}) {
|
||||
const [filter, setFilter] = useState({
|
||||
asDate: moment().endOf('day').format('YYYY-MM-DD'),
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
// Handle refetch vendors balance summary.
|
||||
|
||||
@@ -33,14 +33,20 @@ function VendorsBalanceSummaryHeader({
|
||||
|
||||
// filter form initial values.
|
||||
const defaultValues = {
|
||||
...pageFilter,
|
||||
asDate: moment().toDate(),
|
||||
vendorsIds: [],
|
||||
};
|
||||
// Initial form values.
|
||||
const initialValues = transformToForm({
|
||||
...pageFilter,
|
||||
asDate: moment(pageFilter.asDate).toDate(),
|
||||
}, defaultValues);
|
||||
const initialValues = transformToForm(
|
||||
{
|
||||
...defaultValues,
|
||||
|
||||
...pageFilter,
|
||||
asDate: moment(pageFilter.asDate).toDate(),
|
||||
},
|
||||
defaultValues,
|
||||
);
|
||||
|
||||
// handle form submit.
|
||||
const handleSubmit = (values, { setSubmitting }) => {
|
||||
|
||||
@@ -11,6 +11,8 @@ import {
|
||||
FieldHint,
|
||||
FormattedMessage as T,
|
||||
} from '../../../components';
|
||||
import { filterVendorsOptions } from '../constants';
|
||||
|
||||
import {
|
||||
momentFormatter,
|
||||
tansformDateValue,
|
||||
@@ -18,6 +20,7 @@ import {
|
||||
handleDateChange,
|
||||
} from 'utils';
|
||||
import { useVendorsBalanceSummaryGeneralPanelContext } from './VendorsBalanceSummaryHeaderGeneralProvider';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
|
||||
/**
|
||||
* Vendors balance header - General panel - Content.
|
||||
@@ -71,6 +74,12 @@ export default function VendorsBalanceSummaryHeaderGeneralContent() {
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<FinancialStatementsFilter
|
||||
items={filterVendorsOptions}
|
||||
label={<T id={'vendors.label_filter_vendors'} />}
|
||||
initialSelectedItem={'with-transactions'}
|
||||
/>
|
||||
|
||||
<Row>
|
||||
<Col xs={4}>
|
||||
<Field name={'vendorsIds'}>
|
||||
|
||||
@@ -31,6 +31,7 @@ function VendorsTransactions({
|
||||
const [filter, setFilter] = useState({
|
||||
fromDate: moment().startOf('year').format('YYYY-MM-DD'),
|
||||
toDate: moment().endOf('year').format('YYYY-MM-DD'),
|
||||
filterByOption: 'with-transactions',
|
||||
});
|
||||
|
||||
const handleFilterSubmit = (filter) => {
|
||||
|
||||
@@ -31,23 +31,26 @@ function VendorsTransactionsHeader({
|
||||
}) {
|
||||
// Default form values.
|
||||
const defaultValues = {
|
||||
...pageFilter,
|
||||
fromDate: moment().toDate(),
|
||||
toDate: moment().toDate(),
|
||||
vendorsIds: [],
|
||||
};
|
||||
|
||||
// Initial form values.
|
||||
const initialValues = transformToForm({
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
}, defaultValues);
|
||||
const initialValues = transformToForm(
|
||||
{
|
||||
...defaultValues,
|
||||
...pageFilter,
|
||||
fromDate: moment(pageFilter.fromDate).toDate(),
|
||||
toDate: moment(pageFilter.toDate).toDate(),
|
||||
},
|
||||
defaultValues,
|
||||
);
|
||||
|
||||
// Validation schema.
|
||||
const validationSchema = Yup.object().shape({
|
||||
fromDate: Yup.date()
|
||||
.required()
|
||||
.label(intl.get('fromDate')),
|
||||
fromDate: Yup.date().required().label(intl.get('fromDate')),
|
||||
toDate: Yup.date()
|
||||
.min(Yup.ref('fromDate'))
|
||||
.required()
|
||||
@@ -62,7 +65,9 @@ function VendorsTransactionsHeader({
|
||||
};
|
||||
|
||||
// Handle drawer close action.
|
||||
const handleDrawerClose = () => { toggleFilterDrawer(false); };
|
||||
const handleDrawerClose = () => {
|
||||
toggleFilterDrawer(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<FinancialStatementHeader
|
||||
|
||||
@@ -4,12 +4,16 @@ import classNames from 'classnames';
|
||||
import { Classes, FormGroup } from '@blueprintjs/core';
|
||||
|
||||
import FinancialStatementDateRange from '../FinancialStatementDateRange';
|
||||
import FinancialStatementsFilter from '../FinancialStatementsFilter';
|
||||
|
||||
import {
|
||||
Row,
|
||||
Col,
|
||||
ContactsMultiSelect,
|
||||
FormattedMessage as T,
|
||||
} from '../../../components';
|
||||
import { filterVendorsOptions } from '../constants';
|
||||
|
||||
import {
|
||||
VendorsTransactionsGeneralPanelProvider,
|
||||
useVendorsTransactionsGeneralPanelContext,
|
||||
@@ -35,7 +39,11 @@ function VendorsTransactionsHeaderGeneralPanelContent() {
|
||||
return (
|
||||
<div>
|
||||
<FinancialStatementDateRange />
|
||||
|
||||
<FinancialStatementsFilter
|
||||
items={filterVendorsOptions}
|
||||
label={<T id={'vendors.label_filter_vendors'} />}
|
||||
initialSelectedItem={'all-vendors'}
|
||||
/>
|
||||
<Row>
|
||||
<Col xs={5}>
|
||||
<Field name={'vendorsIds'}>
|
||||
|
||||
@@ -1,72 +1,9 @@
|
||||
import * as R from 'ramda';
|
||||
import intl from 'react-intl-universal';
|
||||
import { displayColumnsByOptions } from './constants';
|
||||
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) => {
|
||||
const columnType = displayColumnsByOptions.find(
|
||||
@@ -85,17 +22,15 @@ export const transformDisplayColumnsType = (form) => {
|
||||
const setNoneZeroTransactions = (form) => {
|
||||
return {
|
||||
...form,
|
||||
noneZero: form.accountsFilter === 'without-zero-balance',
|
||||
noneTransactions: form.accountsFilter === 'with-transactions',
|
||||
noneZero: form.filterByOption === 'without-zero-balance',
|
||||
noneTransactions: form.filterByOption === 'with-transactions',
|
||||
onlyActive: form.filterByOption === 'with-only-active',
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
// filterByOption
|
||||
export const transformAccountsFilter = (form) => {
|
||||
return R.compose(
|
||||
R.omit(['accountsFilter']),
|
||||
setNoneZeroTransactions,
|
||||
)(form)
|
||||
}
|
||||
return R.compose(R.omit(['filterByOption']), setNoneZeroTransactions)(form);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform filter form to http query.
|
||||
|
||||
141
src/containers/FinancialStatements/constants.js
Normal file
141
src/containers/FinancialStatements/constants.js
Normal 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'),
|
||||
},
|
||||
]
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Switch,
|
||||
Alignment,
|
||||
} from '@blueprintjs/core';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
import { DashboardRowsHeightButton, FormattedMessage as T } from 'components';
|
||||
import DashboardActionsBar from 'components/Dashboard/DashboardActionsBar';
|
||||
import Icon from 'components/Icon';
|
||||
import {
|
||||
@@ -25,8 +25,10 @@ import { useRefreshItems } from 'hooks/query/items';
|
||||
import withItems from 'containers/Items/withItems';
|
||||
import withItemsActions from './withItemsActions';
|
||||
import withAlertActions from 'containers/Alert/withAlertActions';
|
||||
import withSettings from '../Settings/withSettings';
|
||||
|
||||
import { compose } from 'utils';
|
||||
import withSettingsActions from '../Settings/withSettingsActions';
|
||||
|
||||
/**
|
||||
* Items actions bar.
|
||||
@@ -42,6 +44,12 @@ function ItemsActionsBar({
|
||||
|
||||
// #withAlertActions
|
||||
openAlert,
|
||||
|
||||
// #withSettings
|
||||
itemsTableSize,
|
||||
|
||||
// #withSettingsActions
|
||||
addSetting,
|
||||
}) {
|
||||
// Items list context.
|
||||
const { itemsViews, fields } = useItemsListContext();
|
||||
@@ -72,10 +80,14 @@ function ItemsActionsBar({
|
||||
const checked = event.target.checked;
|
||||
setItemsTableState({ inactiveMode: checked });
|
||||
};
|
||||
|
||||
// Handle refresh button click.
|
||||
const handleRefreshBtnClick = () => {
|
||||
refresh();
|
||||
};
|
||||
// Handle table row size change.
|
||||
const handleTableRowSizeChange = (size) => {
|
||||
addSetting('items', 'tableSize', size);
|
||||
};
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
@@ -131,6 +143,12 @@ function ItemsActionsBar({
|
||||
icon={<Icon icon="file-export-16" iconSize={16} />}
|
||||
text={<T id={'export'} />}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<DashboardRowsHeightButton
|
||||
initialValue={itemsTableSize}
|
||||
onChange={handleTableRowSizeChange}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
<Switch
|
||||
labelElement={<T id={'inactive'} />}
|
||||
defaultChecked={itemsInactiveMode}
|
||||
@@ -150,11 +168,15 @@ function ItemsActionsBar({
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSettingsActions,
|
||||
withItems(({ itemsSelectedRows, itemsTableState }) => ({
|
||||
itemsSelectedRows,
|
||||
itemsInactiveMode: itemsTableState.inactiveMode,
|
||||
itemsFilterRoles: itemsTableState.filterRoles,
|
||||
})),
|
||||
withSettings(({ itemsSettings }) => ({
|
||||
itemsTableSize: itemsSettings.tableSize,
|
||||
})),
|
||||
withItemsActions,
|
||||
withAlertActions,
|
||||
)(ItemsActionsBar);
|
||||
|
||||
@@ -10,11 +10,11 @@ import TableSkeletonHeader from 'components/Datatable/TableHeaderSkeleton';
|
||||
|
||||
import { TABLES } from 'common/tables';
|
||||
|
||||
import withItems from 'containers/Items/withItems';
|
||||
import withItemsActions from 'containers/Items/withItemsActions';
|
||||
import withAlertsActions from 'containers/Alert/withAlertActions';
|
||||
import withDialogActions from 'containers/Dialog/withDialogActions';
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
import withSettings from '../Settings/withSettings';
|
||||
|
||||
import { useItemsListContext } from './ItemsListProvider';
|
||||
import { useItemsTableColumns, ItemsActionMenuList } from './components';
|
||||
@@ -37,8 +37,8 @@ function ItemsDataTable({
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
|
||||
// #withItems
|
||||
itemsTableState,
|
||||
// #withSettings
|
||||
itemsTableSize,
|
||||
|
||||
// #ownProps
|
||||
tableProps,
|
||||
@@ -146,6 +146,7 @@ function ItemsDataTable({
|
||||
onCellClick={handleCellClick}
|
||||
initialColumnsWidths={initialColumnsWidths}
|
||||
onColumnResizing={handleColumnResizing}
|
||||
size={itemsTableSize}
|
||||
payload={{
|
||||
onDeleteItem: handleDeleteItem,
|
||||
onEditItem: handleEditItem,
|
||||
@@ -167,5 +168,7 @@ export default compose(
|
||||
withAlertsActions,
|
||||
withDrawerActions,
|
||||
withDialogActions,
|
||||
withItems(({ itemsTableState }) => ({ itemsTableState })),
|
||||
withSettings(({ itemsSettings }) => ({
|
||||
itemsTableSize: itemsSettings.tableSize,
|
||||
})),
|
||||
)(ItemsDataTable);
|
||||
|
||||
@@ -34,7 +34,7 @@ const transfromItemsToSearch = (item) => ({
|
||||
id: item.id,
|
||||
text: item.name,
|
||||
subText: item.code,
|
||||
label: item.type,
|
||||
label: item.type_formatted,
|
||||
reference: item,
|
||||
});
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ function ItemsCategoryActionsBar({
|
||||
});
|
||||
};
|
||||
|
||||
console.log(fields, categoriesFilterConditions, 'XXXX');
|
||||
|
||||
|
||||
return (
|
||||
<DashboardActionsBar>
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
|
||||
import { Formik, Form } from 'formik';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import * as R from 'ramda';
|
||||
import intl from 'react-intl-universal';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import { isEmpty } from 'lodash';
|
||||
@@ -16,13 +15,14 @@ import BillItemsEntriesEditor from './BillItemsEntriesEditor';
|
||||
|
||||
import { AppToaster } from 'components';
|
||||
|
||||
import { ERROR } from 'common/errors';
|
||||
import { useBillFormContext } from './BillFormProvider';
|
||||
import { compose, safeSumBy } from 'utils';
|
||||
import {
|
||||
defaultBill,
|
||||
filterNonZeroEntries,
|
||||
transformToEditForm,
|
||||
transformEntriesToSubmit,
|
||||
transformFormValuesToRequest,
|
||||
handleErrors,
|
||||
} from './utils';
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
|
||||
@@ -55,23 +55,12 @@ function BillForm({
|
||||
[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.
|
||||
const handleFormSubmit = (
|
||||
values,
|
||||
{ setSubmitting, setErrors, resetForm },
|
||||
) => {
|
||||
const entries = values.entries.filter(
|
||||
(item) => item.item_id && item.quantity,
|
||||
);
|
||||
const entries = filterNonZeroEntries(values.entries);
|
||||
const totalQuantity = safeSumBy(entries, 'quantity');
|
||||
|
||||
if (totalQuantity === 0) {
|
||||
@@ -83,9 +72,8 @@ function BillForm({
|
||||
return;
|
||||
}
|
||||
const form = {
|
||||
...values,
|
||||
...transformFormValuesToRequest(values),
|
||||
open: submitPayload.status,
|
||||
entries: transformEntriesToSubmit(entries),
|
||||
};
|
||||
// Handle the request success.
|
||||
const onSuccess = (response) => {
|
||||
|
||||
@@ -41,6 +41,12 @@ export const defaultBill = {
|
||||
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.
|
||||
*/
|
||||
@@ -70,11 +76,33 @@ export const transformToEditForm = (bill) => {
|
||||
* Transformes bill entries to submit request.
|
||||
*/
|
||||
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);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
@@ -118,3 +146,24 @@ export const entriesFieldShouldUpdate = (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
Reference in New Issue
Block a user