Compare commits

...

43 Commits

Author SHA1 Message Date
a.bouhuolia
4d00f53600 feat: release v1.7.6-rc.2 version. 2022-04-23 00:46:16 +02:00
a.bouhuolia
5e293e4f19 Merge branch 'develop' into main 2022-04-23 00:42:23 +02:00
Ahmed Bouhuolia
01038136f2 Update CHANGELOG.md 2022-04-23 00:41:18 +02:00
a.bouhuolia
6bc5eec8b6 Merge branch 'develop' into main 2022-04-23 00:38:28 +02:00
a.bouhuolia
1172e69d96 chore: add 1.7.4-rc.2 CHANGELOG. 2022-04-23 00:37:18 +02:00
a.bouhuolia
87758bf773 chore(Sidebar): docs. 2022-04-23 00:28:26 +02:00
a.bouhuolia
5cbb3c84e6 Merge branch 'BIG-374-refactoring-sidebar-menu-with-feature-and-permissions-control' into develop 2022-04-22 23:42:45 +02:00
a.bouhuolia
52924383bd feat(Sidebar): filter sidebar items based on subscription state. 2022-04-18 01:24:11 +02:00
a.bouhuolia
8d1825a065 feature(Sidebar): BIG-374 filtering the sidebar items based on each item feature support. 2022-04-18 00:16:37 +02:00
a.bouhuolia
5e4e9c37c3 feat(Sidebar): add the missing sidebar items. 2022-04-17 05:19:23 +02:00
a.bouhuolia
944bc29f4d feat(Sidebar): Refactoring sidebar menu with feature and permissions abilities control. 2022-04-17 05:05:35 +02:00
a.bouhuolia
682b296f7c fix(FlexGrid): BIG-378 Reports drawers columns css conflict. 2022-04-15 22:33:08 +02:00
Ahmed Bouhuolia
e662bf7af9 Update CHANGELOG.md 2022-04-15 06:34:18 +02:00
a.bouhuolia
a829ceb709 Merge branch 'develop' into main 2022-04-15 06:29:34 +02:00
a.bouhuolia
cdce00187b Merge branch 'feature/tooltip-oneline' into develop 2022-04-15 06:26:42 +02:00
a.bouhuolia
91a38b34cc feat: add readonly entriese details as oneline with tooltip for more details. 2022-04-15 06:24:24 +02:00
a.bouhuolia
1b97a162e8 Merge branch 'BIG-entries-columns' of https://github.com/bigcapitalhq/client into develop 2022-04-15 04:36:08 +02:00
elforjani13
e8e12e63ea feat: add tooltip cell to detail. 2022-04-10 13:46:05 +02:00
elforjani13
80feba6005 feat(sales/purchases): add tooltip cell to detail. 2022-04-09 02:04:31 +02:00
elforjani13
cc457e1e43 feat: estimate tooltip. 2022-04-09 00:42:34 +02:00
a.bouhuolia
2ced5dc013 feat(TextOverlayTooltip): WIP 2022-04-08 06:04:12 +02:00
Ahmed Bouhuolia
dd86c2993e Merge pull request #50 from bigcapitalhq/BIG-372-activate-branches-and-warehouses-dialog-reloading-once-activating
BIG-372: activate branches & warehouses reloading.
2022-04-08 05:21:31 +02:00
a.bouhuolia
bd05a4a188 fix(GeneralLedger): BIG-373 Issue general ledger report select specific account. 2022-04-08 05:19:11 +02:00
elforjani13
8160cbe402 feat: add entries-columns. 2022-04-06 16:52:44 +02:00
elforjani13
0ef6bebfb8 BIG-372: activate branches & warehouses reloading. 2022-04-06 15:00:30 +02:00
a.bouhuolia
91ff3fdccb Merge branch 'main' into develop 2022-04-05 06:08:54 +02:00
a.bouhuolia
edd37fff78 fix(PaymaentViaVocher): make the plan is fixed. 2022-04-05 06:06:02 +02:00
a.bouhuolia
679f7ce96c Merge branch 'develop' into main 2022-04-05 05:27:28 +02:00
a.bouhuolia
5eb9968095 Merge branch 'develop' into main 2022-03-31 15:09:25 +02:00
a.bouhuolia
5c601fcf2d fix(Account): BIG-296 Issue when creating a new child account from chart of accounts list. 2022-03-28 11:58:12 +02:00
a.bouhuolia
df4c4a832b Merge branch 'develop' into main 2022-03-24 15:32:30 +02:00
a.bouhuolia
98a02396a9 Merge branch 'develop' into main 2022-02-21 15:02:35 +02:00
a.bouhuolia
96635ffa84 Merge branch 'develop' into main 2022-02-18 20:44:42 +02:00
a.bouhuolia
68c0678dc3 Merge branch 'develop' into main 2022-02-17 12:10:20 +02:00
a.bouhuolia
da699a766a Merge branch 'develop' into main 2022-01-13 15:40:55 +02:00
a.bouhuolia
f1899e1ce1 Merge branch 'develop' into main 2022-01-08 18:20:08 +02:00
Ahmed Bouhuolia
11851d114d Merge pull request #16 from bigcapitalhq/develop
Release 1.5.5
2022-01-04 22:35:49 +02:00
a.bouhuolia
21779007be fix: application version. 2022-01-03 23:14:11 +02:00
a.bouhuolia
4fc1ecdc2d Merge branch 'main' of https://github.com/bigcapitalhq/client into main 2022-01-03 19:42:48 +02:00
a.bouhuolia
c9b5cecf7a Merge branch 'develop' into main 2022-01-03 19:42:23 +02:00
elforjani13
c31e9dcd29 fix: inventory adjustment & contacts drawer. 2022-01-03 14:35:50 +02:00
elforjani13
430ab95dc3 landed cost localiztion. 2022-01-03 13:38:22 +02:00
Ahmed Bouhuolia
8100a57195 Merge pull request #15 from bigcapitalhq/develop
Merge `develop` to `main`
2022-01-03 12:31:14 +02:00
69 changed files with 2308 additions and 1387 deletions

View File

@@ -2,66 +2,95 @@
All notable changes to Bigcapital server-side will be in this file.
## [1.7.6-rc.2] - 23-04-2022
### Fixed
- `BIG-374` Refactoring sidebar men with ability permissions and feature control on each item.
## [v1.7.5-rc.2] - 20-04-2022
### Fixed.
- `BIG-378` Reports drawers columns css conflict.
## [1.7.3-rc.2] - 15-04-2022
### Fixed
- `BIG-372` Activate branches and warehouses dialog reloading once activating.
- `BIG-373` Issue general ledger report select specific account.
- `BIG-377` Make readonly details entries as oneline with tooltip for more details.
## [1.7.2-rc.2] - 04-04-2022
### Fixed
- Add the missing Arabic localization.
- Subscription plans modifications.
- Add the missing Arabic localization.
- Subscription plans modifications.
## [1.7.1-rc.2] - 30-03-2022
## Added
- `BIG-141` Add inactive status to item drawer details.
- `BIG-278` Add created at date on expense details.
- `BIG-350` Add empty status content of warehouse transfers service.
- `BIG-344` Add branch details to manual journal and expense details.
- `BIG-141` Add inactive status to item drawer details.
- `BIG-278` Add created at date on expense details.
- `BIG-350` Add empty status content of warehouse transfers service.
- `BIG-344` Add branch details to manual journal and expense details.
## Fixed
- `BIG-221` Remove Non-inventory radio choice on item form.
- `BIG-236` Validate estimate expiration date should be equal or bigger than estimate date.
- `BIG-237` Validate invoice due date should be equal or bigger than invoice date.
- `BIG-238` Validate bill due date should be equal or bigger than bill date.
- `BIG-280` Optimize style of multi-select accounts menu.
- `BIG-284` Cashflow statement loading bar.
- `BIG-296` Creating a new child account from accounts list.
- `BIG-301` Navigation bar divider on actions bar hide with permissions control.
- `BIG-304` Adding cash or bank account from cash flow service.
- `BIG-351` Invalid date in the inventory adjustment detail.
- `BIG-352` Fix terms and notes fields on footer of all services.
- `BIG-354` Validate the warehouse transfer quantity should be above zero.
- `BIG-221` Remove Non-inventory radio choice on item form.
- `BIG-236` Validate estimate expiration date should be equal or bigger than estimate date.
- `BIG-237` Validate invoice due date should be equal or bigger than invoice date.
- `BIG-238` Validate bill due date should be equal or bigger than bill date.
- `BIG-280` Optimize style of multi-select accounts menu.
- `BIG-284` Cashflow statement loading bar.
- `BIG-296` Creating a new child account from accounts list.
- `BIG-301` Navigation bar divider on actions bar hide with permissions control.
- `BIG-304` Adding cash or bank account from cash flow service.
- `BIG-351` Invalid date in the inventory adjustment detail.
- `BIG-352` Fix terms and notes fields on footer of all services.
- `BIG-354` Validate the warehouse transfer quantity should be above zero.
## [1.7.0-rc.1] - 24-03-2022
## Added
- Multiply currencies with foreign currencies.
- Multiply warehouses to track inventory items.
- Multiply branches to track organization transactions.
- Transfer orders between warehouses.
- Integrate financial reports with multiply branches.
- Integrate inventory reports with multiply warehouses.
## Added
- Multiply currencies with foreign currencies.
- Multiply warehouses to track inventory items.
- Multiply branches to track organization transactions.
- Transfer orders between warehouses.
- Integrate financial reports with multiply branches.
- Integrate inventory reports with multiply warehouses.
## Changes
- Optimize style of sale invoice form.
- Optimize style of sale receipt form.
- Optimize style of credit note form.
- Optimize style of payment receive form.
- Optimize style of bill form.
- Optimize style of payment made form.
- Optimize style of manual journal form.
- Optimize style of expense form.
- Optimize style of sale invoice form.
- Optimize style of sale receipt form.
- Optimize style of credit note form.
- Optimize style of payment receive form.
- Optimize style of bill form.
- Optimize style of payment made form.
- Optimize style of manual journal form.
- Optimize style of expense form.
## [1.6.3] - 21-02-2022
### Fixed
- `BIG-337` Display billing page once the organization subscription is inactive.
- `BIG-337` Display billing page once the organization subscription is inactive.
## [1.6.2] - 19-02-2022
### Fixed
- fix syled components dependency with imported as default components.
- fix syled components dependency with imported as default components.
## [1.6.0] - 18-02-2022
### Added
- Balance sheet comparison of previous period (PP).
- Balance sheet comparison of previous year (PY).
- Balance sheet percentage analysis columns and rows basis.
@@ -72,10 +101,12 @@ All notable changes to Bigcapital server-side will be in this file.
## [1.5.8] - 13-01-2022
### Added
- Add payment receive PDF print.
- Add credit note PDF print.
### Fixed
- fix: Payment receive initial loading state depends on request loading state instead fetching.
- fix: Balance sheet report alert positioning.
- fix: Separate customer and vendor inactivate and activate alerts.

View File

@@ -85,6 +85,9 @@ function getClientEnvironment(publicUrl) {
WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
// Application version.
VERSION: paths.appVersion
}
);
// Stringify all values so we can feed into webpack DefinePlugin

View File

@@ -48,6 +48,8 @@ const resolveModule = (resolveFn, filePath) => {
return resolveFn(`${filePath}.js`);
};
const appVersion = require(resolveApp('package.json')).version;
// config after eject: we're in ./config/
module.exports = {
dotenv: resolveApp('.env'),
@@ -65,6 +67,7 @@ module.exports = {
proxySetup: resolveApp('src/setupProxy.js'),
appNodeModules: resolveApp('node_modules'),
publicUrlOrPath,
appVersion
};

42
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "bigcapital-client",
"version": "1.5.8",
"version": "1.7.1",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -1043,17 +1043,6 @@
"to-fast-properties": "^2.0.0"
}
},
"@blueprintjs-formik/core": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/core/-/core-0.1.5.tgz",
"integrity": "sha512-H0aXiNMYC8RwhWR1F2O77dcRcRITijUX5we51G4AK2Vmp1yXCmNb0piN9ftsjL5vVIvIsMKWg+dfbwREmB5VWg==",
"requires": {
"lodash.get": "^4.4.2",
"lodash.keyby": "^4.6.0",
"styled-components": "^5.3.3",
"web-vitals": "^2.1.4"
}
},
"@blueprintjs-formik/select": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/@blueprintjs-formik/select/-/select-0.1.4.tgz",
@@ -1980,6 +1969,16 @@
"integrity": "sha512-MUc6zSmU3tEVnkQ78q0peeEjKWPUADMlC/t++2bI8WnAG2tvYRPIgHG8lWkXwqc8MsUF6Z2MOf+Mh5sazOmhiQ==",
"dev": true
},
"@types/hoist-non-react-statics": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz",
"integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
"dev": true,
"requires": {
"@types/react": "*",
"hoist-non-react-statics": "^3.3.0"
}
},
"@types/http-proxy": {
"version": "1.17.3",
"resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.3.tgz",
@@ -2240,6 +2239,25 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
"integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw=="
},
"@types/styled-components": {
"version": "5.1.25",
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.25.tgz",
"integrity": "sha512-fgwl+0Pa8pdkwXRoVPP9JbqF0Ivo9llnmsm+7TCI330kbPIFd9qv1Lrhr37shf4tnxCOSu+/IgqM7uJXLWZZNQ==",
"dev": true,
"requires": {
"@types/hoist-non-react-statics": "*",
"@types/react": "*",
"csstype": "^3.0.2"
},
"dependencies": {
"csstype": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.11.tgz",
"integrity": "sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw==",
"dev": true
}
}
},
"@types/testing-library__dom": {
"version": "6.12.1",
"resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-6.12.1.tgz",

View File

@@ -38,6 +38,7 @@
"cross-env": "^7.0.2",
"css-loader": "3.4.2",
"deep-map-keys": "^2.0.1",
"deepdash": "^5.3.9",
"dependency-graph": "^0.11.0",
"dotenv": "8.2.0",
"dotenv-expand": "5.1.0",
@@ -150,6 +151,7 @@
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.1.8",
"@types/styled-components": "^5.1.25",
"@types/yup": "^0.29.13",
"@welldone-software/why-did-you-render": "^6.0.0-rc.1",
"compression-webpack-plugin": "^6.1.0",

View File

@@ -11,7 +11,7 @@ export const CommercialDocHeader = styled.div`
`;
export const CommercialDocTopHeader = styled.div`
margin-bottom: 25px;
margin-bottom: 30px;
`;
export const CommercialDocEntriesTable = styled(DataTable)`

View File

@@ -3,7 +3,7 @@ import { Switch, Route } from 'react-router';
import 'style/pages/Dashboard/Dashboard.scss';
import Sidebar from 'components/Sidebar/Sidebar';
import { Sidebar } from 'containers/Dashboard/Sidebar/Sidebar';
import DashboardContent from 'components/Dashboard/DashboardContent';
import DialogsContainer from 'components/DialogsContainer';
import PreferencesPage from 'components/Preferences/PreferencesPage';

View File

@@ -0,0 +1,27 @@
import React from 'react';
import { Tooltip, Position } from '@blueprintjs/core';
/**
* Text overview tooltip cell.
* @returns {JSX.Element}
*/
export function TextOverviewTooltipCell({ cell: { value } }) {
const SUBMENU_POPOVER_MODIFIERS = {
flip: { boundariesElement: 'viewport', padding: 20 },
offset: { offset: '0, 10' },
preventOverflow: { boundariesElement: 'viewport', padding: 40 },
};
return (
<Tooltip
content={value}
position={Position.BOTTOM_LEFT}
boundary={'viewport'}
minimal={true}
modifiers={SUBMENU_POPOVER_MODIFIERS}
targetClassName={'table-tooltip-overview-target'}
>
{value}
</Tooltip>
);
}

View File

@@ -10,6 +10,7 @@ import CheckBoxFieldCell from './CheckBoxFieldCell';
import SwitchFieldCell from './SwitchFieldCell';
import TextAreaCell from './TextAreaCell';
import BranchesListFieldCell from './BranchesListFieldCell';
import { TextOverviewTooltipCell } from './TextOverviewTooltipCell';
export {
AccountsListFieldCell,
@@ -25,4 +26,5 @@ export {
SwitchFieldCell,
TextAreaCell,
BranchesListFieldCell,
TextOverviewTooltipCell,
};

View File

@@ -0,0 +1,16 @@
import styled from 'styled-components';
import { FlexProps } from './interfaces';
export const FlexStyled = styled.div<FlexProps>`
display: flex;
justify-content: space-between;
flex-wrap: wrap;
align-items: ${({ align }) => align || 'center'};
&:after {
content: '';
max-width: ${({ col, gap = 1 }) =>
col && col < 12 ? `${(100 * col) / 12 - gap}%` : '100%'};
width: 100%;
}
`;

View File

@@ -0,0 +1,26 @@
import * as React from 'react';
import { FlexProps } from './interfaces';
import { FlexItem } from './FlexItem';
import { FlexStyled } from './Flex.style';
export function Flex({
children,
col = 12,
gap,
align,
className,
style,
}: FlexProps) {
return (
<FlexStyled
col={col}
gap={gap}
align={align}
className={className}
style={style}
>
{children}
<FlexItem col={col} gap={gap} />
</FlexStyled>
);
}

View File

@@ -0,0 +1,19 @@
import styled from 'styled-components';
import { ItemProps } from './interfaces';
export const FlexItem = styled.div<ItemProps>`
width: 100%;
max-width: ${({ col, gap = 1 }) =>
col && col < 12 ? `${(100 * col) / 12 - gap}%` : '100%'};
${({ marginBottom }) =>
marginBottom &&
`
margin-bottom: ${marginBottom}px;
`}
${({ stretch }) =>
stretch &&
`
display: flex;
align-self: stretch;
`}
`;

View File

@@ -0,0 +1 @@
export * from './FlexItem.style';

View File

@@ -0,0 +1,4 @@
export * from './Flex.style';
export * from './Flex';
export * from './FlexItem.style';
export * from './interfaces';

View File

@@ -0,0 +1,22 @@
import { HTMLAttributes, Component, StyleHTMLAttributes } from 'react';
export type Range = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12;
export interface ItemProps extends HTMLAttributes<HTMLDivElement> {
gap?: number;
col: Range;
marginBottom?: number;
stretch?: boolean;
as?: string | Component;
className?: string;
style?: StyleHTMLAttributes<HTMLDivElement>;
}
export interface FlexProps extends HTMLAttributes<HTMLDivElement> {
gap?: number;
align?: 'flex-start' | 'flex-end' | 'center' | 'baseline' | 'stretch';
col?: Range;
className?: string;
style?: StyleHTMLAttributes<HTMLDivElement>;
as?: string | Component;
}

View File

@@ -104,7 +104,7 @@ import {
// textClassName?: string;
// }
export default class MenuItem extends AbstractPureComponent2 {
export class MenuItem extends AbstractPureComponent2 {
static get defaultProps() {
return {
disabled: false,

View File

@@ -1,37 +0,0 @@
import React from 'react';
import SidebarContainer from 'components/Sidebar/SidebarContainer';
import SidebarHead from 'components/Sidebar/SidebarHead';
import SidebarMenu from 'components/Sidebar/SidebarMenu';
import { useGetSidebarMenu } from './utils';
import 'style/containers/Dashboard/Sidebar.scss';
export default function Sidebar({ dashboardContentRef }) {
const menu = useGetSidebarMenu();
return (
<SidebarContainer>
<SidebarHead />
<div className="sidebar__menu">
<SidebarMenu menu={menu} />
</div>
<SidebarFooterVersion />
</SidebarContainer>
);
}
/**
* Sidebar footer version.
* @returns {React.JSX}
*/
function SidebarFooterVersion() {
const { REACT_APP_VERSION: VERSION } = process.env;
if (!VERSION) {
return null;
}
return <div class="sidebar__version">v{VERSION}</div>;
}

View File

@@ -1,123 +0,0 @@
import React from 'react';
import { Menu, MenuDivider } from '@blueprintjs/core';
import { useHistory, useLocation } from 'react-router-dom';
import { Choose } from 'components';
import Icon from 'components/Icon';
import MenuItem from 'components/MenuItem';
import { MenuItemLabel } from 'components';
import classNames from 'classnames';
import SidebarOverlay from 'components/SidebarOverlay';
import { compose } from 'redux';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
const DEFAULT_ITEM = {
text: '',
href: '',
};
function matchPath(pathname, path, matchExact) {
return matchExact ? pathname === path : pathname.indexOf(path) !== -1;
}
function SidebarMenuItemSpace({ space }) {
return <div class="bp3-menu-spacer" style={{ height: `${space}px` }} />;
}
function SidebarMenu({ menu, isSubscriptionActive }) {
const history = useHistory();
const location = useLocation();
const [isOpen, setIsOpen] = React.useState(false);
const [currentItem, setCurrentItem] = React.useState(null);
const menuItemsMapper = (list) => {
return list.map((item, index) => {
const hasChildren = Array.isArray(item.children);
const isActive =
(item.children
? item.children.some((c) =>
matchPath(location.pathname, c.href, item.matchExact),
)
: item.href &&
matchPath(location.pathname, item.href, item.matchExact)) ||
currentItem === item;
const handleItemClick = () => {
if (item.href) {
history.push(item.href);
}
if (item.children && item.children.length > 0) {
setIsOpen(true);
setCurrentItem(item);
} else {
setIsOpen(false);
}
};
return (
<Choose>
<Choose.When condition={item.spacer}>
<SidebarMenuItemSpace space={item.spacer} />
</Choose.When>
<Choose.When condition={item.divider}>
<MenuDivider key={index} title={item.title} />
</Choose.When>
<Choose.When condition={item.label}>
<MenuItemLabel key={index} text={item.text} />
</Choose.When>
<Choose.Otherwise>
<MenuItem
key={index}
active={isActive}
icon={<Icon icon={item.icon} iconSize={item.iconSize} />}
text={item.text}
disabled={item.disabled}
dropdownType={item.dropdownType || 'collapse'}
caretIconSize={16}
onClick={handleItemClick}
callapseActive={!!isActive}
itemClassName={classNames({
'is-active': isActive,
'has-icon': !hasChildren && item.icon,
})}
hasSubmenu={hasChildren}
/>
</Choose.Otherwise>
</Choose>
);
});
};
const filterItems = menu.filter(
(item) => isSubscriptionActive || item.enableBilling,
);
const items = menuItemsMapper(filterItems);
const handleSidebarOverlayClose = () => {
setIsOpen(false);
};
return (
<div>
<Menu className="sidebar-menu">{items}</Menu>{' '}
<SidebarOverlay
isOpen={isOpen}
label={currentItem?.text || ''}
items={currentItem?.children || []}
onClose={handleSidebarOverlayClose}
/>
</div>
);
}
export default compose(
withSubscriptions(
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
'main',
),
)(SidebarMenu);

View File

@@ -1,48 +0,0 @@
import sidebarMenuList from 'config/sidebarMenu';
import { isArray, isEmpty } from 'lodash';
import { useAbilityContext } from 'hooks/utils';
export function useGetSidebarMenu() {
const ability = useAbilityContext();
return sidebarMenuList
.map((item) => {
const children = isArray(item.children)
? item.children.filter((childItem) => {
return isArray(childItem.permission)
? childItem.permission.some((perm) =>
ability.can(perm.ability, perm.subject),
)
: childItem?.permission?.ability && childItem?.permission?.subject
? ability.can(
childItem.permission.ability,
childItem.permission.subject,
)
: true;
})
: [];
return {
...item,
...(isArray(item.children)
? {
children,
}
: {}),
};
})
.filter((item) => {
return isArray(item.permission)
? item.permission.some((per) =>
ability.can(per.ability, per.subject),
)
: item?.permission?.ability && item?.permission?.subject
? ability.can(item.permission.ability, item.permission.subject)
: true;
})
.filter((item) =>
isEmpty(item.children) && !item.href && !item.label && !item.divider
? false
: true,
);
}

View File

@@ -1,146 +0,0 @@
import React from 'react';
import { Overlay } from '@blueprintjs/core';
import { Link } from 'react-router-dom';
import SidebarOverlayContainer from './SidebarOverlayContainer';
interface ISidebarOverlayItem {
text: string;
href: string;
divider?: boolean;
label?: boolean;
}
interface ISidebarOverlayProps {
isOpen: boolean;
items: ISidebarOverlayItem[];
label: string;
onClose: Function;
}
interface ISidebarOverlayItemProps {
text: string;
href: string;
onLinkClick: Function;
}
interface ISidebarOverlayItemDivider {
divider: boolean;
}
/**
* Sidebar overlay item.
*/
function SidebarOverlayItem({
text,
href,
onLinkClick,
}: ISidebarOverlayItemProps) {
const handleLinkClick = () => {
onLinkClick && onLinkClick();
};
return (
<div className="sidebar-overlay__item">
<Link onClick={handleLinkClick} to={href}>
{text}
</Link>
</div>
);
}
interface ISidebarOverlayItemLabel {
text: string;
}
function SidebarOverlayLabel({ text }: ISidebarOverlayItemLabel) {
return <div className="sidebar-overlay__label">{text}</div>;
}
function SidebarOverlayDivider() {
return <div className={'sidebar-overlay__divider'}></div>;
}
/**
* Sidebar overlay component.
*/
export default function SidebarOverlay({
label,
isOpen: controllerdIsOpen,
onClose,
items,
}: ISidebarOverlayProps) {
const [isEverOpened, setEverOpened] = React.useState(false);
const [isOpen, setIsOpen] = React.useState(controllerdIsOpen);
React.useEffect(() => {
if (
typeof controllerdIsOpen !== 'undefined' &&
isOpen !== controllerdIsOpen
) {
setIsOpen(controllerdIsOpen);
}
}, [controllerdIsOpen, setIsOpen, isOpen]);
React.useEffect(() => {
if (isOpen && !isEverOpened) {
setEverOpened(true);
}
}, [isEverOpened, isOpen]);
if (!isEverOpened) {
return '';
}
// Handle overlay close event.
const handleOverlayClose = () => {
setIsOpen(false);
onClose && onClose();
};
// Handle overlay open event.
const handleOverlayOpen = () => {
setIsOpen(true);
};
// Handle sidebar item link click.
const handleItemClick = () => {
setIsOpen(false);
onClose && onClose();
};
return (
<Overlay
isOpen={isOpen}
portalContainer={
(document.querySelector('.Pane.vertical.Pane2') as HTMLElement) ||
document.body
}
onClose={handleOverlayClose}
onOpening={handleOverlayOpen}
transitionDuration={100}
backdropClassName={'sidebar-overlay-backdrop'}
>
<div className="sidebar-overlay sidebar-overlay-transition">
<SidebarOverlayContainer>
<div className="sidebar-overlay__menu">
{label && (
<>
<SidebarOverlayLabel text={label} />
<SidebarOverlayDivider />
</>
)}
{items.map((item) =>
item.divider ? (
<SidebarOverlayDivider />
) : item.label ? (
<SidebarOverlayLabel text={item.text} />
) : (
<SidebarOverlayItem
onLinkClick={handleItemClick}
text={item.text}
href={item.href}
/>
),
)}
</div>
</SidebarOverlayContainer>
</div>
</Overlay>
);
}

View File

@@ -49,7 +49,6 @@ import DrawerHeaderContent from './Drawer/DrawerHeaderContent';
import Postbox from './Postbox';
import AccountsSuggestField from './AccountsSuggestField';
import MaterialProgressBar from './MaterialProgressBar';
import { MoneyFieldCell } from './DataTableCells';
import AvaterCell from './AvaterCell';
import { ItemsMultiSelect } from './Items';
@@ -102,9 +101,12 @@ export * from './ExchangeRate';
export * from './Branches';
export * from './Warehouses';
export * from './Currencies';
export * from './FormTopbar'
export * from './FormTopbar';
export * from './Paper';
export * from './Accounts'
export * from './Accounts';
export * from './DataTableCells';
export * from './FlexGrid';
export * from './MenuItem';
const Hint = FieldHint;
@@ -167,7 +169,6 @@ export {
Postbox,
AccountsSuggestField,
MaterialProgressBar,
MoneyFieldCell,
ItemsMultiSelect,
AvaterCell,
MoreMenuItems,

File diff suppressed because it is too large Load Diff

View File

@@ -57,9 +57,7 @@ function BillTransactionDeleteAlert({
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?`}
/>
<T id={`landed_cost.once_your_delete_this_located_landed_cost`} />
</p>
</Alert>
);

View File

@@ -79,6 +79,10 @@ export const handleCashFlowTransactionType = (reference, openDrawer) => {
return openDrawer('refund-vendor-detail-drawer', {
refundTransactionId: reference.reference_id,
});
case 'InventoryAdjustment':
return openDrawer('inventory-adjustment-drawer', {
inventoryId: reference.reference_id,
});
default:
return openDrawer('cashflow-transaction-drawer', {

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { SidebarContainer } from './SidebarContainer';
import { SidebarHead } from './SidebarHead';
import { SidebarMenu } from './SidebarMenu';
import { useMainSidebarMenu } from './hooks';
import { SidebarOverlayBinded } from '../SidebarOverlay';
import 'style/containers/Dashboard/Sidebar.scss';
/**
* Dashboard sidebar.
* @returns {JSX.Element}
*/
export function Sidebar() {
const menu = useMainSidebarMenu();
return (
<SidebarContainer>
<SidebarHead />
<div className="sidebar__menu">
<SidebarMenu menu={menu} />
</div>
<SidebarOverlayBinded />
<SidebarFooterVersion />
</SidebarContainer>
);
}
/**
* Sidebar footer version.
* @returns {React.JSX}
*/
function SidebarFooterVersion() {
const { VERSION } = process.env;
if (!VERSION) {
return null;
}
return <div class="sidebar__version">v{VERSION}</div>;
}

View File

@@ -1,19 +1,21 @@
import React, { useEffect } from 'react';
import { Scrollbar } from 'react-scrollbars-custom';
import classNames from 'classnames';
import withDashboardActions from 'containers/Dashboard/withDashboardActions';
import withDashboard from 'containers/Dashboard/withDashboard';
import withSubscriptions from 'containers/Subscriptions/withSubscriptions';
import { useObserveSidebarExpendedBodyclass } from './hooks';
import { compose } from 'utils';
import withSubscriptions from '../../containers/Subscriptions/withSubscriptions';
function SidebarContainer({
/**
* Sidebar container/
* @returns {JSX.Element}
*/
function SidebarContainerJSX({
// #ownProps
children,
// #withDashboardActions
toggleSidebarExpend,
// #withDashboard
sidebarExpended,
@@ -22,9 +24,10 @@ function SidebarContainer({
}) {
const sidebarScrollerRef = React.useRef();
useEffect(() => {
document.body.classList.toggle('has-mini-sidebar', !sidebarExpended);
// Toggles classname to body once sidebar expend/shrink.
useObserveSidebarExpendedBodyclass(sidebarExpended);
useEffect(() => {
if (!sidebarExpended && sidebarScrollerRef.current) {
sidebarScrollerRef.current.scrollTo({
top: 0,
@@ -39,9 +42,9 @@ function SidebarContainer({
}
};
const scrollerElementRef = (ref) => {
const scrollerElementRef = React.useCallback((ref) => {
sidebarScrollerRef.current = ref;
};
}, []);
return (
<div
@@ -64,8 +67,7 @@ function SidebarContainer({
);
}
export default compose(
withDashboardActions,
export const SidebarContainer = compose(
withDashboard(({ sidebarExpended }) => ({
sidebarExpended,
})),
@@ -73,4 +75,4 @@ export default compose(
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
'main',
),
)(SidebarContainer);
)(SidebarContainerJSX);

View File

@@ -1,9 +1,11 @@
import React from 'react';
import { Button, Popover, Menu, Position } from '@blueprintjs/core';
import Icon from 'components/Icon';
import { Icon } from 'components';
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
import { useAuthenticatedAccount } from 'hooks/query';
import { compose, firstLettersArgs } from 'utils';
import withCurrentOrganization from '../../containers/Organization/withCurrentOrganization';
import { useAuthenticatedAccount } from '../../hooks/query';
// Popover modifiers.
const POPOVER_MODIFIERS = {
@@ -13,7 +15,7 @@ const POPOVER_MODIFIERS = {
/**
* Sideabr head.
*/
function SidebarHead({
function SidebarHeadJSX({
// #withCurrentOrganization
organization,
}) {
@@ -61,6 +63,6 @@ function SidebarHead({
);
}
export default compose(
export const SidebarHead = compose(
withCurrentOrganization(({ organization }) => ({ organization })),
)(SidebarHead);
)(SidebarHeadJSX);

View File

@@ -0,0 +1,74 @@
import React from 'react';
import { Menu } from '@blueprintjs/core';
import * as R from 'ramda';
import { MenuItem, MenuItemLabel } from 'components';
import { ISidebarMenuItemType } from 'containers/Dashboard/Sidebar/interfaces';
import { useIsSidebarMenuItemActive } from './hooks';
import withSubscriptions from 'containers/Subscriptions/withSubscriptions';
/**
* Sidebar menu item.
* @returns {JSX.Element}
*/
function SidebarMenuItem({ item, index }) {
// Detarmine whether the item is active.
const isActive = useIsSidebarMenuItemActive(item);
return (
<MenuItem
key={index}
text={item.text}
disabled={item.disabled}
dropdownType={item.dropdownType || 'collapse'}
caretIconSize={16}
onClick={item.onClick}
active={isActive}
hasSubmenu={item.hasChildren}
/>
);
}
SidebarMenuItem.ItemTypes = [
ISidebarMenuItemType.Link,
ISidebarMenuItemType.Overlay,
ISidebarMenuItemType.Dialog,
];
/**
* Detarmines which sidebar menu item type should display.
* @returns {JSX.Element}
*/
function SidebarMenuItemComposer({ item, index }) {
// Link item type.
return SidebarMenuItem.ItemTypes.indexOf(item.type) !== -1 ? (
<SidebarMenuItem item={item} index={index} />
) : // Group item type.
item.type === ISidebarMenuItemType.Group ? (
<MenuItemLabel text={item.text} />
) : null;
}
/**
* Sidebar menu.
* @returns {JSX.Element}
*/
function SidebarMenuJSX({ menu }) {
return (
<div>
<Menu className="sidebar-menu">
{menu.map((item, index) => (
<SidebarMenuItemComposer index={index} item={item} />
))}
</Menu>
</div>
);
}
export const SidebarMenu = R.compose(
withSubscriptions(
({ isSubscriptionActive }) => ({ isSubscriptionActive }),
'main',
),
)(SidebarMenuJSX);

View File

@@ -0,0 +1,358 @@
import _, { isEmpty, includes } from 'lodash';
import React from 'react';
import * as R from 'ramda';
import { useHistory } from 'react-router-dom';
import { useAbilityContext } from 'hooks';
import { useSidebarSubmenu, useFeatureCan } from 'hooks/state';
import { SidebarMenu } from 'config/sidebarMenu';
import {
ISidebarMenuItemType,
ISidebarSubscriptionAbility,
} from './interfaces';
import {
useSidebarSubmnuActions,
useDialogActions,
useSubscription,
} from 'hooks/state';
import { filterValuesDeep, deepdash } from 'utils';
const deepDashConfig = {
childrenPath: 'children',
pathFormat: 'array',
};
const ingoreTypesEmpty = [
ISidebarMenuItemType.Group,
ISidebarMenuItemType.Overlay,
];
/**
* Removes the all overlay items from the menu to the main-sidebar.
* @param {ISidebarMenuItem[]} menu
* @returns {ISidebarMenuItem[]}
*/
function removeSidebarOverlayChildren(menu) {
return deepdash.mapValuesDeep(
menu,
(item, key, parent, context) => {
if (item.type === ISidebarMenuItemType.Overlay) {
context.skipChildren(true);
return _.omit(item, ['children']);
}
return item;
},
deepDashConfig,
);
}
/**
* Retrives the main sidebar pre-menu.
* @returns {ISidebarMenuItem[]}
*/
export function getMainSidebarMenu() {
return R.compose(removeSidebarOverlayChildren)(SidebarMenu);
}
/**
* Predicates whether the sidebar item has feature ability.
*/
function useFilterSidebarItemFeaturePredicater() {
const { featureCan } = useFeatureCan();
return {
// Returns false if the item has `feature` prop and that feature has no ability.
predicate: (item) => {
if (item.feature && !featureCan(item.feature)) {
return false;
}
return true;
},
};
}
/**
* Predicates whether the sidebar item has permissio ability.
*/
function useFilterSidebarItemAbilityPredicater() {
const ability = useAbilityContext();
return {
// Retruns false if the item has `permission` prop and that permission has no ability.
predicate: (item) => {
if (
item.permission &&
!ability.can(item.permission.ability, item.permission.subject)
) {
return false;
}
return true;
},
};
}
/**
* Filters the sidebar item based on the subscription state.
*/
function useFilterSidebarItemSubscriptionPredicater() {
const { isSubscriptionActive, isSubscriptionInactive } = useSubscription();
return {
predicate: (item) => {
const { subscription } = item;
if (subscription) {
const isActive = includes(subscription, [
ISidebarSubscriptionAbility.Active,
])
? isSubscriptionActive
: true;
const isInactive = includes(subscription, [
ISidebarSubscriptionAbility.Inactive,
])
? isSubscriptionInactive
: true;
return isActive && isInactive;
}
return true;
},
};
}
/**
* Filters sidebar menu items based on ability of the item permission.
* @param {} menu
* @returns {}
*/
function useFilterSidebarMenuAbility(menu) {
const { predicate: predFeature } = useFilterSidebarItemFeaturePredicater();
const { predicate: predAbility } = useFilterSidebarItemAbilityPredicater();
const { predicate: predSubscription } =
useFilterSidebarItemSubscriptionPredicater();
return deepdash.filterDeep(
menu,
(item) => {
return predFeature(item) && predAbility(item) && predSubscription(item);
},
deepDashConfig,
);
}
/**
* Flats the sidebar menu groups.
* @param {*} menu
* @returns {}
*/
function useFlatSidebarMenu(menu) {
return React.useMemo(() => {
return deepdash.mapDeep(menu, (item) => item, deepDashConfig);
}, [menu]);
}
/**
* Binds sidebar link item click action.
* @param {ISidebarMenuItem} item
*/
function useBindSidebarItemLinkClick() {
const history = useHistory();
const { closeSidebarSubmenu } = useSidebarSubmnuActions();
// Handle sidebar item click.
const onClick = (item) => (event) => {
closeSidebarSubmenu();
history.push(item.href);
};
return {
bindOnClick: (item) => {
return {
...item,
onClick: onClick(item),
};
},
};
}
/**
* Bind sidebar dialog item click action.
* @param {ISidebarMenuItem} item
*/
function useBindSidebarItemDialogClick() {
const { closeSidebarSubmenu } = useSidebarSubmnuActions();
const { openDialog } = useDialogActions();
// Handle sidebar item click.
const onClick = (item) => (event) => {
closeSidebarSubmenu();
openDialog(item.dialogName, item.dialogPayload);
};
return {
bindOnClick: (item) => {
return {
...item,
onClick: onClick(item),
};
},
};
}
/**
* Binds click action for the sidebar overlay item.
*/
function useBindSidebarItemOverlayClick() {
const { toggleSidebarSubmenu, closeSidebarSubmenu } =
useSidebarSubmnuActions();
// Handle sidebar item click.
const onClick = (item) => (event) => {
closeSidebarSubmenu();
toggleSidebarSubmenu({ submenuId: item.overlayId });
};
return {
bindOnClick: (item) => {
return {
...item,
onClick: onClick(item),
};
},
};
}
/**
* Binds click action of the given sidebar menu for each item based on item type.
*/
function useBindSidebarItemClick(menu) {
const { bindOnClick: bindLinkClickEvt } = useBindSidebarItemLinkClick();
const { bindOnClick: bindOverlayClickEvt } = useBindSidebarItemOverlayClick();
const { bindOnClick: bindItemDialogEvt } = useBindSidebarItemDialogClick();
return React.useMemo(() => {
return deepdash.mapValuesDeep(
menu,
(item) => {
return R.compose(
R.when(
R.propSatisfies(R.equals(ISidebarMenuItemType.Link), 'type'),
bindLinkClickEvt,
),
R.when(
R.propSatisfies(R.equals(ISidebarMenuItemType.Overlay), 'type'),
bindOverlayClickEvt,
),
R.when(
R.propSatisfies(R.equals(ISidebarMenuItemType.Dialog), 'type'),
bindItemDialogEvt,
),
)(item);
},
deepDashConfig,
);
}, [menu]);
}
/**
* Finds the given overlay submenu id from the menu graph.
* @param {ISidebarMenuOverlayIds}
* @param {ISidebarMenuItem[]} menu -
* @returns {ISidebarMenuItem[]}
*/
const findSubmenuBySubmenuId = R.curry((submenuId, menu) => {
const groupItem = deepdash.findDeep(
menu,
(item) => {
return (
item.type === ISidebarMenuItemType.Overlay &&
item.overlayId === submenuId
);
},
deepDashConfig,
);
return groupItem?.value?.children || [];
});
/**
* Retrieves the main sidebar post-menu.
* @returns {ISidebarMenuItem[]}
*/
export function useMainSidebarMenu() {
return R.compose(
useBindSidebarItemClick,
useFlatSidebarMenu,
removeSidebarOverlayChildren,
useAssocSidebarItemHasChildren,
filterSidebarItemHasNoChildren,
useFilterSidebarMenuAbility,
)(SidebarMenu);
}
/**
* Assoc `hasChildren` prop to sidebar menu items.
* @param {ISidebarMenuItem[]} items
* @returns {ISidebarMenuItem[]}
*/
function useAssocSidebarItemHasChildren(items) {
return deepdash.mapValuesDeep(
items,
(item) => {
return {
...item,
hasChildren: !isEmpty(item.children),
};
},
deepDashConfig,
);
}
/**
* Retrieves the sub-sidebar post-menu.
* @param {ISidebarMenuOverlayIds} submenuId
* @returns {ISidebarMenuItem[]}
*/
export function useSubSidebarMenu(submenuId) {
if (!submenuId) return [];
return R.compose(
useBindSidebarItemClick,
useFlatSidebarMenu,
filterSidebarItemHasNoChildren,
useFilterSidebarMenuAbility,
findSubmenuBySubmenuId(submenuId),
)(SidebarMenu);
}
/**
* Observes the sidebar expending with body class.
* @param {boolean} sidebarExpended
*/
export function useObserveSidebarExpendedBodyclass(sidebarExpended) {
React.useEffect(() => {
document.body.classList.toggle('has-mini-sidebar', !sidebarExpended);
}, [sidebarExpended]);
}
/**
* Detamrines whether the given sidebar menu item is active.
* @returns {boolean}
*/
export function useIsSidebarMenuItemActive(item) {
const { submenuId } = useSidebarSubmenu();
return (
item.type === ISidebarMenuItemType.Overlay && submenuId === item.overlayId
);
}
/**
* Filter sidebar specific items types that have no types.
* @param {ISidebarMenuItem[]} items -
* @returns {ISidebarMenuItem[]}
*/
export function filterSidebarItemHasNoChildren(items) {
return filterValuesDeep((item) => {
// If it was group item and has no children items so discard that item.
if (ingoreTypesEmpty.indexOf(item.type) !== -1 && isEmpty(item.children)) {
return false;
}
return true;
}, items);
}

View File

@@ -0,0 +1,77 @@
export enum ISidebarMenuItemType {
Label = 'label',
Link = 'link',
Group = 'group',
Overlay = 'overlay',
Dialog = 'dialog',
Drawer = 'drawer',
}
export interface ISidebarMenuItemOverlay extends ISidebarMenuItemCommon {
type: ISidebarMenuItemType.Overlay;
}
export interface ISidebarMenuItemLink extends ISidebarMenuItemCommon {
text: string | JSX.Element;
href: string;
type: ISidebarMenuItemType.Link;
matchExact?: boolean;
}
export interface ISidebarMenuItemDialog extends ISidebarMenuItemCommon {
type: ISidebarMenuItemType.Dialog;
dialogName: string;
dialogPayload: any;
}
export interface ISidebarMenuItemDrawer extends ISidebarMenuItemCommon {
type: ISidebarMenuItemType.Drawer;
drawerName: string;
drawerPayload: any;
}
export interface ISidebarMenuItemLabel extends ISidebarMenuItemCommon {
text?: string;
type: ISidebarMenuItemType.Label;
}
export interface ISidebarMenuItemGroup extends ISidebarMenuItemCommon {
type: ISidebarMenuItemType.Group;
}
export interface ISidebarMenuItemPermission {
subject: string;
ability: string;
}
export interface ISidebarMenuItemCommon {
ability?: ISidebarMenuItemPermission | ISidebarMenuItemPermission[];
feature?: string;
disabled?: boolean;
children?: ISidebarMenuItem[];
onlySubscriptionExpired?: boolean;
}
export type ISidebarMenuItem =
| ISidebarMenuItemLink
| ISidebarMenuItemLabel
| ISidebarMenuItemGroup
| ISidebarMenuItemOverlay
| ISidebarMenuItemDialog
| ISidebarMenuItemDrawer;
export enum ISidebarMenuOverlayIds {
Items = 'Items',
Reports = 'Reports',
Sales = 'Sales',
Purchases = 'Purchases',
Financial = 'Financial',
Contacts = 'Contacts',
Cashflow = 'Cashflow',
Expenses = 'Expenses',
}
export enum ISidebarSubscriptionAbility {
Expired = 'SubscriptionExpired',
Active = 'SubscriptionActive',
}

View File

@@ -0,0 +1,13 @@
import { connect } from 'react-redux';
export default (mapState) => {
const mapStateToProps = (state, props) => {
const mapped = {
sidebarSubmenuOpen: state.dashboard.sidebarSubmenu.isOpen,
sidebarSubmenuId: state.dashboard.sidebarSubmenu.submenuId,
};
return mapState ? mapState(mapped, state, props) : mapped;
};
return connect(mapStateToProps);
}

View File

@@ -0,0 +1,16 @@
import { connect } from 'react-redux';
import {
closeSidebarSubmenu,
openSidebarSubmenu,
} from 'store/dashboard/dashboard.actions';
const mapActionsToProps = (dispatch) => ({
// Opens the dashboard submenu sidebar.
openDashboardSidebarSubmenu: (submenuId) =>
dispatch(openSidebarSubmenu(submenuId)),
// Closes the dashboard submenu sidebar.
closeDashboardSidebarSubmenu: () => dispatch(closeSidebarSubmenu()),
});
export default connect(null, mapActionsToProps);

View File

@@ -0,0 +1,142 @@
//@ts-nocheck
import React from 'react';
import { Overlay, OverlayProps } from '@blueprintjs/core';
import { Link } from 'react-router-dom';
import { SidebarOverlayContainer } from './SidebarOverlayContainer';
import { ISidebarMenuItem, ISidebarMenuItemType } from '../Sidebar/interfaces';
export interface ISidebarOverlayItem {
text: string;
href: string;
divider?: boolean;
label?: boolean;
}
export interface ISidebarOverlayProps {
items: ISidebarMenuItem[];
}
export interface ISidebarOverlayItemProps {
text: string | JSX.Element;
href?: string;
onClick?: any;
}
export interface ISidebarOverlayItemDivider {
divider: boolean;
}
/**
* Sidebar overlay item.
* @param {ISidebarOverlayItemProps}
* @returns {JSX.Element}
*/
export function SidebarOverlayItemLink({
text,
href,
onClick,
}: ISidebarOverlayItemProps) {
return (
<div className="sidebar-overlay__item">
<Link to={href} onClick={onClick}>
{text}
</Link>
</div>
);
}
export interface ISidebarOverlayItemLabel {
text: string;
}
/**
* Sidebar overlay label item.
* @param {ISidebarOverlayItemLabel}
* @returns {JSX.Element}
*/
export function SidebarOverlayLabel({
text,
}: ISidebarOverlayItemLabel): JSX.Element {
return <div className="sidebar-overlay__label">{text}</div>;
}
/**
* Sidebar overlay divider item.
* @returns {JSX.Element}
*/
export function SidebarOverlayDivider() {
return <div className={'sidebar-overlay__divider'}></div>;
}
interface SidebarOverlayItemProps {
item: ISidebarMenuItem;
}
/**
* Sidebar overlay item.
* @param {SidebarOverlayItemProps} props -
* @returns {JSX.Element}
*/
function SidebarOverlayItem({ item }: SidebarOverlayItemProps) {
//
return item.type === ISidebarMenuItemType.Group ? (
<SidebarOverlayLabel text={item.text} />
) : //
item.type === ISidebarMenuItemType.Link ||
item.type === ISidebarMenuItemType.Dialog ? (
<SidebarOverlayItemLink text={item.text} onClick={item.onClick} />
) : null;
}
/**
*
*/
export interface ISidebarOverlayMenu {
items: ISidebarMenuItem[];
}
/**
* Sidebar overlay menu.
* @param {ISidebarOverlayMenu}
* @returns {JSX.Element}
*/
function SidebarOverlayMenu({ items }: ISidebarOverlayMenu) {
return (
<div className="sidebar-overlay__menu">
{items.map((item) => (
<SidebarOverlayItem item={item} />
))}
</div>
);
}
export interface SidebarOverlayProps extends OverlayProps {
items: ISidebarMenuItem[];
}
/**
* Sidebar overlay component.
* @param {SidebarOverlayProps}
* @returns {JSX.Element}
*/
export function SidebarOverlay({ items, label, ...rest }: SidebarOverlayProps) {
return (
<Overlay
portalContainer={
(document.querySelector('.Pane.vertical.Pane2') as HTMLElement) ||
document.body
}
transitionDuration={100}
backdropClassName={'sidebar-overlay-backdrop'}
{...rest}
>
<div className="sidebar-overlay sidebar-overlay-transition">
<SidebarOverlayContainer>
{label && <SidebarOverlayLabel text={label} />}
<SidebarOverlayMenu items={items} />
</SidebarOverlayContainer>
</div>
</Overlay>
);
}

View File

@@ -0,0 +1,54 @@
// @ts-nocheck
import React from 'react';
import * as R from 'ramda';
import { SidebarOverlay } from './SidebarOverlay';
import withDashboardSidebarActions from 'containers/Dashboard/Sidebar/withDashboardSidebarActions';
import withDashboardSidebar from 'containers/Dashboard/Sidebar/withDashboardSidebar';
import { useSubSidebarMenu } from '../Sidebar/hooks';
/**
* Dashboard sidebar menu.
* @returns {JSX.Element}
*/
function SidebarOverlayBindedRoot({
// #withDashboardSidebar
sidebarSubmenuOpen,
sidebarSubmenuId,
// #withDashboardSidebarActions
closeDashboardSidebarSubmenu,
}) {
const handleSidebarClosing = React.useCallback(() => {
closeDashboardSidebarSubmenu();
}, []);
return (
<SidebarOverlayBindedRouter
sidebarSubmenuId={sidebarSubmenuId}
isOpen={sidebarSubmenuOpen}
onClose={handleSidebarClosing}
/>
);
}
/**
* Dashboard sidebar submenu router.
*/
function SidebarOverlayBindedRouter({ sidebarSubmenuId, ...rest }) {
const sidebarItems = useSubSidebarMenu(sidebarSubmenuId);
return <SidebarOverlay items={sidebarItems} {...rest} />;
}
/**
* Sidebar overlay binded with redux.
*/
export const SidebarOverlayBinded = R.compose(
withDashboardSidebar(({ sidebarSubmenuOpen, sidebarSubmenuId }) => ({
sidebarSubmenuOpen,
sidebarSubmenuId,
})),
withDashboardSidebarActions,
)(SidebarOverlayBindedRoot);

View File

@@ -1,14 +1,16 @@
import React from 'react';
import { Scrollbar } from 'react-scrollbars-custom';
interface ISidebarOverlayContainerProps {
children: JSX.Element | JSX.Element[],
export interface ISidebarOverlayContainerProps {
children: JSX.Element | JSX.Element[];
}
/**
* Sidebar overlay container.
*/
export default function SidebarOverlayContainer({ children }: ISidebarOverlayContainerProps) {
export function SidebarOverlayContainer({
children,
}: ISidebarOverlayContainerProps) {
return (
<div className={'sidebar-overlay__scroll-wrapper'}>
<Scrollbar noDefaultStyles={true}>

View File

@@ -0,0 +1,3 @@
export * from './SidebarOverlay';
export * from './SidebarOverlayContainer';
export * from './SidebarOverlayBinded'

View File

@@ -37,7 +37,7 @@ function AllocateLandedCostFloatingActions({
<DialogFooterActions alignment={'left'}>
{costTransactionEntry && (
<UnallocatedAmount>
Unallocated cost Amount:{' '}
<T id={'landed_cost.dialog.label_unallocated_cost_amount'}/>
<strong>{formattedUnallocatedCostAmount}</strong>
</UnallocatedAmount>
)}

View File

@@ -42,7 +42,10 @@ function AllocateLandedCostForm({
.map((entry) => transformToForm(entry, defaultInitialValues.items[0]));
if (entries.length <= 0) {
AppToaster.show({ message: 'Something wrong!', intent: Intent.DANGER });
AppToaster.show({
message: intl.get('something_wrong'),
intent: Intent.DANGER,
});
return;
}
const form = {
@@ -69,13 +72,14 @@ function AllocateLandedCostForm({
)
) {
AppToaster.show({
message:
'The total located cost is bigger than the transaction line.',
message: intl.get(
'landed_cost.error.the_total_located_cost_is_bigger_than_the_transaction_line',
),
intent: Intent.DANGER,
});
} else {
AppToaster.show({
message: 'Something went wrong!',
message: intl.get('something_went_wrong'),
intent: Intent.DANGER,
});
}

View File

@@ -9,7 +9,7 @@ const BranchActivateContext = React.createContext();
* Branch activate form provider.
*/
function BranchActivateFormProvider({ dialogName, ...props }) {
const { mutateAsync: activateBranches, isLoading } = useActivateBranches();
const { mutateAsync: activateBranches } = useActivateBranches();
// State provider.
const provider = {
@@ -18,7 +18,7 @@ function BranchActivateFormProvider({ dialogName, ...props }) {
};
return (
<DialogContent isLoading={isLoading}>
<DialogContent>
<BranchActivateContext.Provider value={provider} {...props} />
</DialogContent>
);

View File

@@ -36,7 +36,7 @@ function PaymentViaLicenseDialogContent({
setSubmitting(true);
const mutateValues = {
plan_slug: `${values.plan_slug}-${values.period}ly`,
plan_slug: `essentials-monthly`,
license_code: values.license_code,
};
// Payment via voucher mutate.

View File

@@ -9,8 +9,7 @@ const WarehouseActivateContext = React.createContext();
* warehouse activate form provider.
*/
function WarehouseActivateFormProvider({ dialogName, ...props }) {
const { mutateAsync: activateWarehouses, isLoading } =
useActivateWarehouses();
const { mutateAsync: activateWarehouses } = useActivateWarehouses();
// State provider.
const provider = {
@@ -19,7 +18,7 @@ function WarehouseActivateFormProvider({ dialogName, ...props }) {
};
return (
<DialogContent isLoading={isLoading}>
<DialogContent>
<WarehouseActivateContext.Provider value={provider} {...props} />
</DialogContent>
);

View File

@@ -13,57 +13,81 @@ import {
} from '@blueprintjs/core';
import {
FormatNumberCell,
TextOverviewTooltipCell,
FormattedMessage as T,
Choose,
Icon,
} from '../../../components';
import { getColumnWidth } from 'utils';
import { useBillDrawerContext } from './BillDrawerProvider';
/**
* Retrieve bill readonly details entries table columns.
*/
export const useBillReadonlyEntriesTableColumns = () =>
React.useMemo(
export const useBillReadonlyEntriesTableColumns = () => {
const {
bill: { entries },
} = useBillDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
Cell: TextOverviewTooltipCell,
width: 150,
className: 'item',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('description'),
accessor: 'description',
Cell: TextOverviewTooltipCell,
className: 'description',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'rate', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'amount', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
],
[],
);
};
/**
* Bill details status.

View File

@@ -10,56 +10,81 @@ import {
Tag,
Intent,
} from '@blueprintjs/core';
import { getColumnWidth } from 'utils';
import {
Icon,
FormattedMessage as T,
TextOverviewTooltipCell,
FormatNumberCell,
Choose,
} from '../../../components';
import { useCreditNoteDetailDrawerContext } from './CreditNoteDetailDrawerProvider';
export const useCreditNoteReadOnlyEntriesColumns = () =>
React.useMemo(
export const useCreditNoteReadOnlyEntriesColumns = () => {
// credit note details drawer context.
const {
creditNote: { entries },
} = useCreditNoteDetailDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
Cell: TextOverviewTooltipCell,
width: 150,
className: 'name',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('description'),
accessor: 'description',
Cell: TextOverviewTooltipCell,
className: 'description',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'rate', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'amount', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
],
[],
);
};
/**
* Credit note more actions mneu.

View File

@@ -1,47 +1,74 @@
import React from 'react';
import intl from 'react-intl-universal';
import { FormatNumberCell } from '../../../components';
import { FormatNumberCell, TextOverviewTooltipCell } from '../../../components';
import { getColumnWidth } from 'utils';
import { useEstimateDetailDrawerContext } from './EstimateDetailDrawerProvider';
/**
* Retrieve table columns of estimate readonly entries details.
*/
export const useEstimateReadonlyEntriesColumns = () =>
React.useMemo(() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
width: 150,
className: 'name',
disableSortBy: true,
},
{
Header: intl.get('description'),
accessor: 'description',
className: 'description',
disableSortBy: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
},
], []);
export const useEstimateReadonlyEntriesColumns = () => {
// estimate details drawer context.
const {
estimate: { entries },
} = useEstimateDetailDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
Cell: TextOverviewTooltipCell,
width: 150,
className: 'name',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('description'),
accessor: 'description',
Cell: TextOverviewTooltipCell,
className: 'description',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'rate', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'amount', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
],
[],
);
};

View File

@@ -1,33 +1,46 @@
import React from 'react';
import intl from 'react-intl-universal';
import { FormatNumberCell } from '../../../components';
import { FormatNumberCell, TextOverviewTooltipCell } from '../../../components';
import { useExpenseDrawerContext } from './ExpenseDrawerProvider';
import { getColumnWidth } from 'utils';
/**
* Retrieve expense readonly details entries table columns.
*/
export const useExpenseReadEntriesColumns = () =>
React.useMemo(
export const useExpenseReadEntriesColumns = () => {
// Expense drawer context.
const {
expense: { categories },
} = useExpenseDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('expense_account'),
accessor: 'expense_account.name',
Cell: TextOverviewTooltipCell,
width: 110,
disableSortBy: true,
textOverview: true,
className: 'account',
},
{
Header: intl.get('description'),
accessor: 'description',
width: 110,
disableSortBy: true,
Cell: TextOverviewTooltipCell,
className: 'description',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(categories, 'amount', {
minWidth: 60,
magicSpacing: 5,
}),
disableSortBy: true,
className: 'amount',
align: 'right',
@@ -35,3 +48,4 @@ export const useExpenseReadEntriesColumns = () =>
],
[],
);
};

View File

@@ -1,37 +1,60 @@
import React from 'react';
import intl from 'react-intl-universal';
import { getColumnWidth } from 'utils';
import { TextOverviewTooltipCell } from 'components';
import { useInventoryAdjustmentDrawerContext } from './InventoryAdjustmentDrawerProvider';
export const useInventoryAdjustmentEntriesColumns = () =>
React.useMemo(
export const useInventoryAdjustmentEntriesColumns = () => {
// Inventory adjustment details drawer context.
const {
inventoryAdjustment: { entries },
} = useInventoryAdjustmentDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('inventory_adjustment.column.product'),
accessor: 'item.name',
width: 150,
Cell: TextOverviewTooltipCell,
width: 100,
className: 'name',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
width: 100,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('cost'),
accessor: 'cost',
width: 100,
width: getColumnWidth(entries, 'cost', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('value'),
accessor: 'value',
width: 100,
width: getColumnWidth(entries, 'value', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
],
[],
);
};

View File

@@ -11,12 +11,14 @@ import {
Intent,
Tag,
} from '@blueprintjs/core';
import { getColumnWidth } from 'utils';
import {
FormatNumberCell,
Icon,
FormattedMessage as T,
Choose,
Can,
TextOverviewTooltipCell,
} from 'components';
import {
SaleInvoiceAction,
@@ -27,49 +29,69 @@ import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
/**
* Retrieve invoice readonly details table columns.
*/
export const useInvoiceReadonlyEntriesColumns = () =>
React.useMemo(
export const useInvoiceReadonlyEntriesColumns = () => {
// Invoice details drawer context.
const {
invoice: { entries },
} = useInvoiceDetailDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
width: 150,
className: 'name',
Cell: TextOverviewTooltipCell,
disableSortBy: true,
textOverview: true,
width: 150,
},
{
Header: intl.get('description'),
accessor: 'description',
className: 'description',
Cell: TextOverviewTooltipCell,
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
textOverview: true,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
textOverview: true,
width: getColumnWidth(entries, 'rate', {
minWidth: 60,
magicSpacing: 5,
}),
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true,
textOverview: true,
width: getColumnWidth(entries, 'amount', {
minWidth: 60,
magicSpacing: 5,
}),
},
],
[],
);
};
/**
* Invoice details more actions menu.

View File

@@ -10,7 +10,10 @@ import { TableStyle } from '../../../common';
* Manual journal drawer table.
*/
export default function ManualJournalDrawerTable() {
// Retrieves the readonly manual journal entries columns.
const columns = useManualJournalEntriesColumns();
// Manual journal drawer context.
const { manualJournal } = useManualJournalDrawerContext();
return (

View File

@@ -1,28 +1,17 @@
import intl from 'react-intl-universal';
import React from 'react';
import { Tag, Intent, Classes, Tooltip, Position } from '@blueprintjs/core';
import { Tag, Intent } from '@blueprintjs/core';
import { T, Choose, FormatNumberCell, If, Icon } from '../../../components';
import {
T,
Choose,
FormatNumberCell,
TextOverviewTooltipCell,
} from '../../../components';
import { Features } from 'common';
import { getColumnWidth } from 'utils';
import { useFeatureCan } from 'hooks/state';
/**
* Note column accessor.
*/
export function NoteAccessor(row) {
return (
<If condition={row.note}>
<Tooltip
className={Classes.TOOLTIP_INDICATOR}
content={row.note}
position={Position.LEFT_TOP}
hoverOpenDelay={50}
>
<Icon icon={'file-alt'} iconSize={16} />
</Tooltip>
</If>
);
}
import { useManualJournalDrawerContext } from './ManualJournalDrawerProvider';
/**
* Publish column accessor.
@@ -50,37 +39,45 @@ export function ManualJournalDetailsStatus({ manualJournal }) {
*/
export const useManualJournalEntriesColumns = () => {
const { featureCan } = useFeatureCan();
// manual journal details drawer context.
const {
manualJournal: { entries },
} = useManualJournalDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('account_name'),
Cell: TextOverviewTooltipCell,
accessor: 'account.name',
width: 130,
disableSortBy: true,
className: 'account',
textOverview: true,
},
{
Header: intl.get('contact'),
accessor: 'contact.display_name',
width: 130,
Cell: TextOverviewTooltipCell,
width: 100,
disableSortBy: true,
className: 'contact',
textOverview: true,
},
{
Header: intl.get('note'),
accessor: NoteAccessor,
width: 80,
accessor: 'note',
Cell: TextOverviewTooltipCell,
disableSortBy: true,
className: 'note',
textOverview: true,
width: 100,
},
...(featureCan(Features.Branches)
? [
{
Header: intl.get('branch'),
width: 130,
width: 100,
accessor: 'branch.name',
disableSortBy: true,
className: 'branch',
},
]
: []),
@@ -88,25 +85,31 @@ export const useManualJournalEntriesColumns = () => {
Header: intl.get('credit'),
accessor: 'credit',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'credit', {
minWidth: 60,
magicSpacing: 5,
}),
disableResizable: true,
disableSortBy: true,
textOverview: true,
formatNumber: { noZero: true },
className: 'credit',
align: 'right',
},
{
Header: intl.get('debit'),
accessor: 'debit',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'debit', {
minWidth: 60,
magicSpacing: 5,
}),
disableResizable: true,
textOverview: true,
disableSortBy: true,
formatNumber: { noZero: true },
className: 'debit',
align: 'right',
},
],
[featureCan],
[],
);
};

View File

@@ -3,43 +3,65 @@ import intl from 'react-intl-universal';
import moment from 'moment';
import { FormatNumberCell } from '../../../components';
import { getColumnWidth } from 'utils';
import { usePaymentMadeDetailContext } from './PaymentMadeDetailProvider';
export const usePaymentMadeEntriesColumns = () =>
React.useMemo(() => [
{
Header: intl.get('date'),
accessor: (row) => moment(row.date).format('YYYY MMM DD'),
width: 100,
disableSortBy: true,
className: 'date',
},
{
Header: intl.get('bill_number'),
accessor: 'bill_no',
width: 150,
disableSortBy: true,
className: 'bill_number',
},
{
Header: intl.get('bill_amount'),
accessor: 'bill.amount',
Cell: FormatNumberCell,
align: 'right',
},
{
Header: intl.get('due_amount'),
accessor: 'bill.due_amount',
Cell: FormatNumberCell,
width: 100,
disableSortBy: true,
align: 'right',
},
{
Header: intl.get('payment_amount'),
accessor: 'payment_amount',
Cell: FormatNumberCell,
width: 100,
disableSortBy: true,
align: 'right',
},
], []);
export const usePaymentMadeEntriesColumns = () => {
// Payment made details context.
const {
paymentMade: { entries },
} = usePaymentMadeDetailContext();
return React.useMemo(
() => [
{
Header: intl.get('date'),
accessor: (row) => moment(row.date).format('YYYY MMM DD'),
width: 100,
disableSortBy: true,
className: 'date',
},
{
Header: intl.get('bill_number'),
accessor: 'bill_no',
width: 150,
disableSortBy: true,
className: 'bill_number',
},
{
Header: intl.get('bill_amount'),
accessor: 'bill.amount',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'bill.amount', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
},
{
Header: intl.get('due_amount'),
accessor: 'bill.due_amount',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'bill.due_amount', {
minWidth: 60,
magicSpacing: 5,
}),
disableSortBy: true,
align: 'right',
},
{
Header: intl.get('payment_amount'),
accessor: 'payment_amount',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'payment_amount', {
minWidth: 60,
magicSpacing: 5,
}),
disableSortBy: true,
textOverview: true,
align: 'right',
},
],
[],
);
};

View File

@@ -2,12 +2,18 @@ import React from 'react';
import intl from 'react-intl-universal';
import moment from 'moment';
import { FormatNumberCell } from '../../../components';
import { getColumnWidth } from 'utils';
import { usePaymentReceiveDetailContext } from './PaymentReceiveDetailProvider';
/**
* Retrieve payment entries table columns.
*/
export const usePaymentReceiveEntriesColumns = () =>
React.useMemo(
export const usePaymentReceiveEntriesColumns = () => {
const {
paymentReceive: { entries },
} = usePaymentReceiveDetailContext();
return React.useMemo(
() => [
{
Header: intl.get('date'),
@@ -27,24 +33,38 @@ export const usePaymentReceiveEntriesColumns = () =>
Header: intl.get('invoice_amount'),
accessor: 'invoice.balance',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'invoice.balance', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
textOverview: true,
},
{
Header: intl.get('amount_due'),
accessor: 'invoice.due_amount',
Cell: FormatNumberCell,
align: 'right',
width: 100,
width: getColumnWidth(entries, 'invoice.due_amount', {
minWidth: 60,
magicSpacing: 5,
}),
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('payment_amount'),
accessor: 'invoice.payment_amount',
Cell: FormatNumberCell,
align: 'right',
width: 100,
width: getColumnWidth(entries, 'invoice.payment_amount', {
minWidth: 60,
magicSpacing: 5,
}),
disableSortBy: true,
textOverview: true,
},
],
[],
);
};

View File

@@ -1,44 +1,69 @@
import React from 'react';
import intl from 'react-intl-universal';
import { FormatNumberCell } from '../../../components';
import { getColumnWidth } from 'utils';
import { FormatNumberCell, TextOverviewTooltipCell } from '../../../components';
import { useReceiptDetailDrawerContext } from './ReceiptDetailDrawerProvider.js';
export const useReceiptReadonlyEntriesTableColumns = () => React.useMemo(() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
width: 150,
className: 'name',
disableSortBy: true
},
{
Header: intl.get('description'),
accessor: 'description',
className: 'description',
disableSortBy: true
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
align: 'right',
disableSortBy: true
},
], []);
export const useReceiptReadonlyEntriesTableColumns = () => {
// Receipt details drawer context.
const {
receipt: { entries },
} = useReceiptDetailDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
Cell: TextOverviewTooltipCell,
width: 150,
className: 'name',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('description'),
accessor: 'description',
Cell: TextOverviewTooltipCell,
className: 'description',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'rate', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: getColumnWidth(entries, 'amount', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
],
[],
);
};

View File

@@ -10,59 +10,82 @@ import {
Tag,
Intent,
} from '@blueprintjs/core';
import { getColumnWidth } from 'utils';
import {
Icon,
FormattedMessage as T,
TextOverviewTooltipCell,
FormatNumberCell,
Choose,
} from '../../../components';
import { useVendorCreditDetailDrawerContext } from './VendorCreditDetailDrawerProvider';
/**
* Retrieve vendor credit readonly details entries table columns.
*/
export const useVendorCreditReadonlyEntriesTableColumns = () =>
React.useMemo(
export const useVendorCreditReadonlyEntriesTableColumns = () => {
const {
vendorCredit: { entries },
} = useVendorCreditDetailDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('product_and_service'),
accessor: 'item.name',
Cell: TextOverviewTooltipCell,
width: 150,
className: 'item',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('description'),
accessor: 'description',
Cell: TextOverviewTooltipCell,
className: 'description',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('rate'),
accessor: 'rate',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'rate', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('amount'),
accessor: 'amount',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'amount', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
textOverview: true,
},
],
[],
);
};
/**
* Vendor note more actions menu.

View File

@@ -1,40 +1,58 @@
import React from 'react';
import intl from 'react-intl-universal';
import { Intent, Tag } from '@blueprintjs/core';
import { getColumnWidth } from 'utils';
import { useWarehouseDetailDrawerContext } from './WarehouseTransferDetailDrawerProvider';
import {
FormattedMessage as T,
FormatNumberCell,
TextOverviewTooltipCell,
Choose,
} from '../../../components';
export const useWarehouseTransferReadOnlyEntriesColumns = () =>
React.useMemo(
/**
* Retrieves the readonly warehouse transfer entries columns.
*/
export const useWarehouseTransferReadOnlyEntriesColumns = () => {
const {
warehouseTransfer: { entries },
} = useWarehouseDetailDrawerContext();
return React.useMemo(
() => [
{
Header: intl.get('warehouse_transfer.column.item_name'),
accessor: 'item.name',
width: 150,
Cell: TextOverviewTooltipCell,
width: 100,
className: 'name',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('warehouse_transfer.column.description'),
accessor: 'description',
Cell: TextOverviewTooltipCell,
className: 'description',
disableSortBy: true,
textOverview: true,
},
{
Header: intl.get('warehouse_transfer.column.transfer_quantity'),
accessor: 'quantity',
Cell: FormatNumberCell,
width: 100,
width: getColumnWidth(entries, 'quantity', {
minWidth: 60,
magicSpacing: 5,
}),
align: 'right',
disableSortBy: true,
},
],
[],
);
};
/**
* Warehouses transfer details status.

View File

@@ -4,7 +4,7 @@ import { FastField, Field } from 'formik';
import { FormGroup, Checkbox } from '@blueprintjs/core';
import styled from 'styled-components';
import { Row, Col, FieldHint, FormattedMessage as T } from 'components';
import { Flex, FlexItem, FieldHint, FormattedMessage as T } from 'components';
import {
handlePreviousYearCheckBoxChange,
handlePreviousYearChangeCheckboxChange,
@@ -15,11 +15,11 @@ import {
} from './utils';
/**
* Balance sheet header - Comparison panal.
* Balance sheet header - Comparison panal - Comparisons fields.
*/
export default function BalanceSheetHeaderComparisonPanal() {
function BalanceSheetHeaderComparisonPanalFields() {
return (
<BalanceSheetComparisonWrap>
<>
{/**----------- Previous Year -----------*/}
<Field name={'previousYear'} type={'checkbox'}>
{({ form, field }) => (
@@ -33,8 +33,9 @@ export default function BalanceSheetHeaderComparisonPanal() {
</FormGroup>
)}
</Field>
<Row>
<Col xs={3}>
<FlexSubFields align={'left'}>
<FlexItem col={6}>
<Field name={'previousYearAmountChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -48,8 +49,9 @@ export default function BalanceSheetHeaderComparisonPanal() {
</FormGroup>
)}
</Field>
</Col>
<Col xs={3}>
</FlexItem>
<FlexItem col={6}>
<FastField name={'previousYearPercentageChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -62,8 +64,9 @@ export default function BalanceSheetHeaderComparisonPanal() {
</FormGroup>
)}
</FastField>
</Col>
</Row>
</FlexItem>
</FlexSubFields>
{/*------------ Previous Period -----------*/}
<FastField name={'previousPeriod'} type={'checkbox'}>
{({ form, field }) => (
@@ -78,8 +81,9 @@ export default function BalanceSheetHeaderComparisonPanal() {
</FormGroup>
)}
</FastField>
<Row>
<Col xs={3}>
<FlexSubFields>
<FlexItem col={6}>
<FastField name={'previousPeriodAmountChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -93,8 +97,9 @@ export default function BalanceSheetHeaderComparisonPanal() {
</FormGroup>
)}
</FastField>
</Col>
<Col xs={3}>
</FlexItem>
<FlexItem col={6}>
<FastField name={'previousPeriodPercentageChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -107,8 +112,8 @@ export default function BalanceSheetHeaderComparisonPanal() {
</FormGroup>
)}
</FastField>
</Col>
</Row>
</FlexItem>
</FlexSubFields>
{/**----------- % of Column -----------*/}
<FastField name={'percentageOfColumn'} type={'checkbox'}>
@@ -137,19 +142,33 @@ export default function BalanceSheetHeaderComparisonPanal() {
</FormGroup>
)}
</FastField>
</>
);
}
/**
* Balance sheet header - Comparison panal.
*/
export default function BalanceSheetHeaderComparisonPanal() {
return (
<BalanceSheetComparisonWrap>
<BalanceSheetComparisonFieldsWrap>
<BalanceSheetHeaderComparisonPanalFields />
</BalanceSheetComparisonFieldsWrap>
</BalanceSheetComparisonWrap>
);
}
const BalanceSheetComparisonWrap = styled.div`
.row {
margin-left: 0.15rem;
.col {
min-width: 150px !important;
max-width: 190px !important;
}
}
.bp3-form-group {
margin-bottom: 3px;
}
`;
const FlexSubFields = styled(Flex)`
padding-left: 20px;
`;
const BalanceSheetComparisonFieldsWrap = styled.div`
width: 400px;
`;

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { FormGroup, Classes } from '@blueprintjs/core';
import { Classes } from '@blueprintjs/core';
import { FormattedMessage as T } from 'components';
import classNames from 'classnames';
import { AccountMultiSelect, Row, Col } from 'components';
import { FFormGroup } from '../../../components/Forms';
@@ -47,10 +46,10 @@ function GLHeaderGeneralPaneContent() {
<Col xs={4}>
<FFormGroup
label={<T id={'specific_accounts'} />}
name={'accounts'}
name={'accountsIds'}
className={Classes.FILL}
>
<AccountMultiSelect name="accounts" accounts={accounts} />
<AccountMultiSelect name="accountsIds" accounts={accounts} />
</FFormGroup>
</Col>
</Row>

View File

@@ -32,7 +32,7 @@ export const getDefaultGeneralLedgerQuery = () => {
basis: 'accural',
filterByOption: 'with-transactions',
branchesIds: [],
accounts: [],
accountsIds: [],
};
};
@@ -49,8 +49,9 @@ const parseGeneralLedgerQuery = (locationQuery) => {
return {
...transformed,
// Ensures the branches ids is always array.
// Ensures the branches, accounts ids is always array.
branchesIds: castArray(transformed.branchesIds),
accountsIds: castArray(transformed.accountsIds),
};
};

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { FastField, Field } from 'formik';
import { FastField } from 'formik';
import { FormGroup, Checkbox } from '@blueprintjs/core';
import styled from 'styled-components';
import { FormattedMessage as T } from 'components';
import { Flex, FlexItem, FormattedMessage as T } from 'components';
import { Row, Col, FieldHint } from '../../../components';
import { FieldHint } from '../../../components';
import {
handlePreviousYearCheckBoxChange,
handlePreviousPeriodCheckBoxChange,
@@ -16,11 +16,12 @@ import {
} from './utils';
/**
* ProfitLoss sheet header -comparison panel.
* Profit/loss comparisons panel fields.
* @returns {JSX.Element}
*/
export default function ProfitLossSheetHeaderComparisonPanel() {
function ProfitLossComaprsionPanelFields() {
return (
<ProfitLossSheetComparisonWrap>
<>
{/**----------- Previous Year -----------*/}
<FastField name={'previousYear'} type={'checkbox'}>
{({ form, field }) => (
@@ -35,8 +36,9 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
<Row>
<Col xs={3}>
<FlexSubFields>
<FlexItem col={6}>
<FastField name={'previousYearAmountChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -50,8 +52,8 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
</Col>
<Col xs={3}>
</FlexItem>
<FlexItem col={6}>
<FastField name={'previousYearPercentageChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -65,8 +67,9 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
</Col>
</Row>
</FlexItem>
</FlexSubFields>
{/**----------- Previous Period (PP) -----------*/}
<FastField name={'previousPeriod'} type={'checkbox'}>
{({ form, field }) => (
@@ -81,8 +84,9 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
<Row>
<Col xs={3}>
<FlexSubFields>
<FlexItem col={6}>
<FastField name={'previousPeriodAmountChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -96,8 +100,8 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
</Col>
<Col xs={3}>
</FlexItem>
<FlexItem col={6}>
<FastField name={'previousPeriodPercentageChange'} type={'checkbox'}>
{({ form, field }) => (
<FormGroup labelInfo={<FieldHint />}>
@@ -111,8 +115,9 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
</Col>
</Row>
</FlexItem>
</FlexSubFields>
{/**----------- % of Column -----------*/}
<FastField name={'percentageColumn'} type={'checkbox'}>
{({ field }) => (
@@ -126,6 +131,7 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
{/**----------- % of Row -----------*/}
<FastField name={'percentageRow'} type={'checkbox'}>
{({ field }) => (
@@ -139,6 +145,7 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
{/**----------- % of Expense -----------*/}
<FastField name={'percentageExpense'} type={'checkbox'}>
{({ field }) => (
@@ -152,6 +159,7 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
{/**----------- % of Income -----------*/}
<FastField name={'percentageIncome'} type={'checkbox'}>
{({ field }) => (
@@ -165,19 +173,33 @@ export default function ProfitLossSheetHeaderComparisonPanel() {
</FormGroup>
)}
</FastField>
</>
);
}
/**
* ProfitLoss sheet header -comparison panel.
*/
export default function ProfitLossSheetHeaderComparisonPanel() {
return (
<ProfitLossSheetComparisonWrap>
<ProfitLossComaprsionFieldsWrap>
<ProfitLossComaprsionPanelFields />
</ProfitLossComaprsionFieldsWrap>
</ProfitLossSheetComparisonWrap>
);
}
const ProfitLossSheetComparisonWrap = styled.div`
.row {
margin-left: 0.15rem;
.col {
min-width: 150px !important;
max-width: 190px !important;
}
}
.bp3-form-group {
margin-bottom: 3px;
}
`;
const FlexSubFields = styled(Flex)`
padding-left: 20px;
`;
const ProfitLossComaprsionFieldsWrap = styled.div`
max-width: 400px;
`;

View File

@@ -12,7 +12,6 @@ function VendorsBalanceSummaryProvider({ filter, ...props }) {
const query = React.useMemo(() => transformFilterFormToQuery(filter), [
filter,
]);
// Fetching vendors balance summary report based on the given query.
const {
data: VendorBalanceSummary,

View File

@@ -1,9 +1,14 @@
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { useDispatch, useSelector } from 'react-redux';
import { createSelector } from 'reselect';
import {
splashStopLoading,
splashStartLoading,
dashboardPageTitle,
openSidebarSubmenu,
closeSidebarSubmenu,
openDialog,
closeDialog,
} from '../../store/dashboard/dashboard.actions';
export const useDispatchAction = (action) => {
@@ -30,3 +35,44 @@ export const useSplashLoading = () => {
useDispatchAction(splashStopLoading),
];
};
/**
* Sidebar submenu actions.
*/
export const useSidebarSubmnuActions = () => {
return {
openSidebarSubmenu: useDispatchAction(openSidebarSubmenu),
closeSidebarSubmenu: useDispatchAction(closeSidebarSubmenu),
toggleSidebarSubmenu: useDispatchAction(openSidebarSubmenu),
};
};
/**
* Retrieves the sidebar submenu selector.
*/
const sidebarSubmenuSelector = createSelector(
(state) => state.dashboard.sidebarSubmenu,
(sidebarSubmenu) => sidebarSubmenu,
);
/**
* Retrieves the sidebar submenu selector.
*/
export const useSidebarSubmenu = () => {
const sidebarSubmenu = useSelector(sidebarSubmenuSelector);
return {
isOpen: sidebarSubmenu?.isOpen || false,
submenuId: sidebarSubmenu?.submenuId || null,
};
};
/**
* Dialogs actions.
*/
export const useDialogActions = () => {
return {
openDialog: useDispatchAction(openDialog),
closeDialog: useDispatchAction(closeDialog),
};
};

View File

@@ -1173,7 +1173,6 @@
"From transaction": "من معاملة",
"Landed": "Landed",
"This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.": "يتيح لك هذا الخيار إمكانية إضافة تكلفة إضافية على سبيل المثال اضافة تكلفة الشحن ومن ثم تخصيص التكلفة لفواتير الشراء.",
"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?": "بمجرد حذف معاملة تحميل التكلفة ، لن تتمكن من استعادتها لاحقًا ، هل أنت متأكد من أنك تريد حذف هذه المعاملة؟",
"journal_entries": "القيود",
"contact": "جهة الاتصال",
"invoice_details": "تفاصيل الفاتورة",
@@ -1390,7 +1389,7 @@
"filter.value": "قيمة",
"payment_made.empty_status.title": "المنشأة لم تدفع اي اموال إلي الموردين ، إلي حد الأن!.",
"estimate.delete.error.estimate_converted_to_invoice": "لا يمكن حذف عملية عرض اسعار الذي تم تحويلها إلي فاتورة بيع.",
"landed_cost.action.delete.success_message": "The landed cost has been deleted successfully.",
"landed_cost.action.delete.success_message": "تم حذف تكلفة اضافية بنجاح. ",
"items.option.only_active": "Only active",
"items.option_all_items.hint": "جميع الاصناف ، بما في ذلك تلك الاصناف لديها رصيد صفر.",
"items.option_with_transactions": "الاصناف مع معاملات",
@@ -1594,6 +1593,9 @@
"refund": "استرجاع",
"landed_cost.dialog.label_select_transaction": "حدد المعاملة ",
"landed_cost.dialog.label_select_transaction_entry": "حدد سطر المعاملة ",
"landed_cost.dialog.label_unallocated_cost_amount":"قيمة التكلفة غير المحملة:",
"landed_cost.error.the_total_located_cost_is_bigger_than_the_transaction_line":"إجمالي قيمة التكلفة المحملة أكبر من قيمة سطر المعاملة.",
"landed_cost.once_your_delete_this_located_landed_cost": "بمجرد حذف معاملة تحميل التكلفة ، لن تتمكن من استعادتها لاحقًا ، هل أنت متأكد من أنك تريد حذف هذه المعاملة؟",
"refund_credit_note.dialog.label": "استرجاع اموال",
"refund_credit_note.dialog.success_message": "تم انشاء معاملة استرجاع الاموال لإشعار الدائن بنجاح.",
"refund_credit_note.dialog.refund_date": "تاريخ الاسترجاع",

View File

@@ -1147,7 +1147,6 @@
"From transaction": "From transaction",
"landed": "Landed",
"This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.": "This options allows you to be able to add additional cost eg. freight then allocate cost to the items in your bills.",
"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?": "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?",
"journal_entries": "Journal Entries",
"contact": "Contact",
"invoice_details": "Invoice details",
@@ -1320,11 +1319,11 @@
"inventory_adjustment.details_drawer.title": "Inventory adjustment details",
"setup.organization.location": "Location",
"preferences.general.success_message": "The general preferences has been saved.",
"customer.drawer.action.new_invoice": "New invoice",
"customer.drawer.action.new_estimate": "New estimate",
"customer.drawer.action.new_payment": "New payment",
"customer.drawer.action.new_receipt": "New receipt",
"customer.drawer.action.new_transaction": "New transaction",
"customer.drawer.action.new_invoice": "New Invoice",
"customer.drawer.action.new_estimate": "New Estimate",
"customer.drawer.action.new_payment": "New Payment",
"customer.drawer.action.new_receipt": "New Receipt",
"customer.drawer.action.new_transaction": "New Transaction",
"customer.drawer.action.edit_opening_balance": "Edit Opening Balance",
"customer.drawer.action.edit": "Edit",
"customer.drawer.label.outstanding_receivable": "Outstanding receivable",
@@ -1351,9 +1350,9 @@
"vendor.drawer.label.note": "Note",
"vendor.drawer.action.edit_vendor": "Edit vendor",
"vendor.drawer.action.delete": "Delete",
"vendor.drawer.action.new_transaction": "New transaction",
"vendor.drawer.action.new_payment": "New payment",
"vendor.drawer.action.new_invoice": "New purchase invoice",
"vendor.drawer.action.new_transaction": "New Transaction",
"vendor.drawer.action.new_payment": "New Payment",
"vendor.drawer.action.new_invoice": "New Purchase Invoice",
"vendor.drawer.action.edit": "Edit",
"vendor.drawer.action.edit_opening_balance": "Edit Opening Balance",
"manual_journals.empty_status.description": "Manual journals can be used to record financial transactions manually, used by accountants to work with the ledger.",
@@ -1612,6 +1611,9 @@
"refund": "Refund",
"landed_cost.dialog.label_select_transaction": "Select transaction",
"landed_cost.dialog.label_select_transaction_entry": "Select transaction entry",
"landed_cost.dialog.label_unallocated_cost_amount": "Unallocated cost Amount:",
"landed_cost.error.the_total_located_cost_is_bigger_than_the_transaction_line": "The total located cost is bigger than the transaction line.",
"landed_cost.once_your_delete_this_located_landed_cost": "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?",
"refund_credit_note.dialog.label": "Refund Credit Note",
"refund_credit_note.dialog.success_message": "The customer credit note refund has been created successfully.",
"refund_credit_note.dialog.refund_date": "Refund date",

View File

@@ -111,3 +111,16 @@ export const setFeatureDashboardMeta = ({ features }) => {
},
};
};
export function openSidebarSubmenu({ submenuId }) {
return {
type: t.SIDEBAR_SUBMENU_OPEN,
payload: { submenuId },
};
}
export function closeSidebarSubmenu() {
return {
type: t.SIDEBAR_SUBMENU_CLOSE,
};
}

View File

@@ -20,10 +20,8 @@ const initialState = {
splashScreenLoading: null,
appIsLoading: true,
appIntlIsLoading: true,
features: {
// branches: true,
// warehouses: true,
},
sidebarSubmenu: { isOpen: false, submenuId: null },
features: {},
};
const STORAGE_KEY = 'bigcapital:dashboard';
@@ -131,6 +129,16 @@ const reducerInstance = createReducer(initialState, {
state.splashScreenLoading = Math.max(state.splashScreenLoading, 0);
},
[t.SIDEBAR_SUBMENU_OPEN]: (state, action) => {
state.sidebarSubmenu.isOpen = true;
state.sidebarSubmenu.submenuId = action.payload.submenuId;
},
[t.SIDEBAR_SUBMENU_CLOSE]: (state, action) => {
state.sidebarSubmenu.isOpen = false;
state.sidebarSubmenu.submenuId = null;
},
[t.RESET]: () => {
purgeStoredState(CONFIG);
},

View File

@@ -13,6 +13,9 @@ export default {
ALTER_DASHBOARD_PAGE_SUBTITLE: 'ALTER_DASHBOARD_PAGE_SUBTITLE',
SET_TOPBAR_EDIT_VIEW: 'SET_TOPBAR_EDIT_VIEW',
SIDEBAR_EXPEND_TOGGLE: 'SIDEBAR_EXPEND_TOGGLE',
SIDEBAR_SUBMENU_OPEN: 'SIDEBAR_SUBMENU_OPEN',
SIDEBAR_SUBMENU_CLOSE: 'SIDEBAR_SUBMENU_CLOSE',
SIDEBAR_SUBMENU_TOGGLE: 'SIDEBAR_SUBMENU_TOGGLE',
SET_DASHBOARD_BACK_LINK: 'SET_DASHBOARD_BACK_LINK',
SPLASH_START_LOADING: 'SPLASH_START_LOADING',
SPLASH_STOP_LOADING: 'SPLASH_STOP_LOADING',

View File

@@ -290,3 +290,8 @@ html[lang^='ar'] {
.font-bold {
font-weight: 600;
}
span.table-tooltip-overview-target{
display: inline;
}

View File

@@ -75,6 +75,8 @@
padding: 14px 20px 10px;
letter-spacing: 1px;
color: #707a85;
border-bottom: 1px solid #e2e5ec;
margin-bottom: 6px;
html[lang^="ar"] & {
font-size: 13px;

33
src/utils/deep.js Normal file
View File

@@ -0,0 +1,33 @@
import _ from 'lodash';
import Deepdash from 'deepdash';
export const deepdash = Deepdash(_);
export const filterValuesDeep = (predicate, nodes) => {
return deepdash.condense(
deepdash.reduceDeep(
nodes,
(accumulator, value, key, parent, context) => {
const newValue = { ...value };
if (newValue.children) {
_.set(newValue, 'children', deepdash.condense(value.children));
}
const isTrue = predicate(newValue, key, parent, context);
if (isTrue === true) {
_.set(accumulator, context.path, newValue);
} else if (isTrue === false) {
_.unset(accumulator, context.path);
}
return accumulator;
},
[],
{
childrenPath: 'children',
pathFormat: 'array',
callbackAfterIterate: true,
},
),
);
};

View File

@@ -14,6 +14,8 @@ import { isEqual } from 'lodash';
import jsCookie from 'js-cookie';
export * from './deep';
export const getCookie = (name, defaultValue) =>
_.defaultTo(jsCookie.get(name), defaultValue);