mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 21:30:31 +00:00
Merge branch 'develop' into main
This commit is contained in:
18
src/components/BaseCurrency.js
Normal file
18
src/components/BaseCurrency.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
import styled from 'styled-components';
|
||||
import { CurrencyTag } from 'components';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
|
||||
/**
|
||||
* base currecncy.
|
||||
*/
|
||||
function BaseCurrency({
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
return <CurrencyTag>{base_currency}</CurrencyTag>;
|
||||
}
|
||||
|
||||
export default R.compose(withCurrentOrganization())(BaseCurrency);
|
||||
124
src/components/BranchSuggestField.js
Normal file
124
src/components/BranchSuggestField.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { MenuItem } from '@blueprintjs/core';
|
||||
import { Suggest } from '@blueprintjs/select';
|
||||
import { FormattedMessage as T } from 'components';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
/**
|
||||
* branch suggest field.
|
||||
* @returns
|
||||
*/
|
||||
export default function BranchSuggestField({
|
||||
branches,
|
||||
initialBranchId,
|
||||
selectedBranchId,
|
||||
defaultSelectText = intl.get('select_branch'),
|
||||
popoverFill = false,
|
||||
onBranchSelected,
|
||||
...suggestProps
|
||||
}) {
|
||||
const initialBranch = React.useMemo(
|
||||
() => branches.find((b) => b.id === initialBranchId),
|
||||
[initialBranchId, branches],
|
||||
);
|
||||
|
||||
const [selectedBranch, setSelectedBranch] = React.useState(
|
||||
initialBranch || null,
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof selectedBranchId !== 'undefined') {
|
||||
const branch = selectedBranchId
|
||||
? branches.find((a) => a.id === selectedBranchId)
|
||||
: null;
|
||||
setSelectedBranch(branch);
|
||||
}
|
||||
}, [selectedBranchId, branches, setSelectedBranch]);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} branch
|
||||
* @returns
|
||||
*/
|
||||
const branchItemRenderer = (branch, { handleClick, modifiers, query }) => {
|
||||
return (
|
||||
<MenuItem
|
||||
// active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
label={branch.code}
|
||||
key={branch.id}
|
||||
onClick={handleClick}
|
||||
text={branch.name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} query
|
||||
* @param {*} branch
|
||||
* @param {*} _index
|
||||
* @param {*} exactMatch
|
||||
* @returns
|
||||
*/
|
||||
const branchItemPredicate = (query, branch, _index, exactMatch) => {
|
||||
const normalizedTitle = branch.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${branch.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} branch
|
||||
* @returns
|
||||
*/
|
||||
const brnachItemSelect = React.useCallback(
|
||||
(branch) => {
|
||||
if (branch.id) {
|
||||
setSelectedBranch({ ...branch });
|
||||
onBranchSelected && onBranchSelected(branch);
|
||||
}
|
||||
},
|
||||
[setSelectedBranch, onBranchSelected],
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} inputVaue
|
||||
* @returns
|
||||
*/
|
||||
const branchInputValueRenderer = (inputValue) => {
|
||||
if (inputValue) {
|
||||
return inputValue.name.toString();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
return (
|
||||
<Suggest
|
||||
items={branches}
|
||||
noResults={<MenuItem disabled={true} text={<T id={'no_accounts'} />} />}
|
||||
itemRenderer={branchItemRenderer}
|
||||
itemPredicate={branchItemPredicate}
|
||||
onItemSelect={brnachItemSelect}
|
||||
selectedItem={selectedBranch}
|
||||
inputProps={{ placeholder: defaultSelectText }}
|
||||
resetOnClose={true}
|
||||
fill={true}
|
||||
popoverProps={{ minimal: true, boundary: 'window' }}
|
||||
inputValueRenderer={branchInputValueRenderer}
|
||||
className={classNames(CLASSES.FORM_GROUP_LIST_SELECT, {
|
||||
[CLASSES.SELECT_LIST_FILL_POPOVER]: popoverFill,
|
||||
})}
|
||||
{...suggestProps}
|
||||
/>
|
||||
);
|
||||
}
|
||||
72
src/components/Branches/BranchMultiSelect.js
Normal file
72
src/components/Branches/BranchMultiSelect.js
Normal file
@@ -0,0 +1,72 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { MenuItem } from '@blueprintjs/core';
|
||||
import { FMultiSelect } from '../Forms';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} query
|
||||
* @param {*} branch
|
||||
* @param {*} _index
|
||||
* @param {*} exactMatch
|
||||
* @returns
|
||||
*/
|
||||
const branchItemPredicate = (query, branch, _index, exactMatch) => {
|
||||
const normalizedTitle = branch.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${branch.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} branch
|
||||
* @param {*} param1
|
||||
* @returns
|
||||
*/
|
||||
const branchItemRenderer = (
|
||||
branch,
|
||||
{ handleClick, modifiers, query },
|
||||
{ isSelected },
|
||||
) => {
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
icon={isSelected ? 'tick' : 'blank'}
|
||||
text={branch.name}
|
||||
label={branch.code}
|
||||
key={branch.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const branchSelectProps = {
|
||||
itemPredicate: branchItemPredicate,
|
||||
itemRenderer: branchItemRenderer,
|
||||
valueAccessor: (item) => item.id,
|
||||
labelAccessor: (item) => item.code,
|
||||
tagRenderer: (item) => item.name,
|
||||
};
|
||||
|
||||
/**
|
||||
* branches mulit select.
|
||||
* @param {*} param0
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function BranchMultiSelect({ branches, ...rest }) {
|
||||
return (
|
||||
<FMultiSelect
|
||||
items={branches}
|
||||
placeholder={intl.get('branches_multi_select.placeholder')}
|
||||
popoverProps={{ minimal: true }}
|
||||
{...branchSelectProps}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
69
src/components/Branches/BranchSelect.js
Normal file
69
src/components/Branches/BranchSelect.js
Normal file
@@ -0,0 +1,69 @@
|
||||
import React from 'react';
|
||||
|
||||
import { MenuItem, Button } from '@blueprintjs/core';
|
||||
import { FSelect } from '../Forms';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} query
|
||||
* @param {*} branch
|
||||
* @param {*} _index
|
||||
* @param {*} exactMatch
|
||||
* @returns
|
||||
*/
|
||||
const branchItemPredicate = (query, branch, _index, exactMatch) => {
|
||||
const normalizedTitle = branch.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${branch.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} film
|
||||
* @param {*} param1
|
||||
* @returns
|
||||
*/
|
||||
const branchItemRenderer = (branch, { handleClick, modifiers, query }) => {
|
||||
const text = `${branch.name}`;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
label={branch.code}
|
||||
key={branch.id}
|
||||
onClick={handleClick}
|
||||
text={text}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const branchSelectProps = {
|
||||
itemPredicate: branchItemPredicate,
|
||||
itemRenderer: branchItemRenderer,
|
||||
valueAccessor: 'id',
|
||||
labelAccessor: 'name',
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export function BranchSelect({ branches, ...rest }) {
|
||||
return <FSelect {...branchSelectProps} {...rest} items={branches} />;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export function BranchSelectButton({ label, ...rest }) {
|
||||
return <Button text={label} {...rest} />;
|
||||
}
|
||||
2
src/components/Branches/index.js
Normal file
2
src/components/Branches/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './BranchSelect';
|
||||
export * from './BranchMultiSelect'
|
||||
10
src/components/Currencies/BaseCurrency.js
Normal file
10
src/components/Currencies/BaseCurrency.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import { CurrencyTag } from 'components';
|
||||
|
||||
/**
|
||||
* base currecncy.
|
||||
* @returns
|
||||
*/
|
||||
export function BaseCurrency({ currency }) {
|
||||
return <CurrencyTag>{currency}</CurrencyTag>;
|
||||
}
|
||||
76
src/components/Currencies/CurrencySelect.js
Normal file
76
src/components/Currencies/CurrencySelect.js
Normal file
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { MenuItem, Button } from '@blueprintjs/core';
|
||||
import { FSelect } from '../Forms';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} query
|
||||
* @param {*} currency
|
||||
* @param {*} _index
|
||||
* @param {*} exactMatch
|
||||
* @returns
|
||||
*/
|
||||
const currencyItemPredicate = (query, currency, _index, exactMatch) => {
|
||||
const normalizedTitle = currency.currency_code.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return (
|
||||
`${currency.currency_code}. ${normalizedTitle}`.indexOf(
|
||||
normalizedQuery,
|
||||
) >= 0
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {*} currency
|
||||
* @returns
|
||||
*/
|
||||
const currencyItemRenderer = (currency, { handleClick, modifiers, query }) => {
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
text={currency.currency_name}
|
||||
label={currency.currency_code.toString()}
|
||||
key={currency.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const currencySelectProps = {
|
||||
itemPredicate: currencyItemPredicate,
|
||||
itemRenderer: currencyItemRenderer,
|
||||
valueAccessor: 'currency_code',
|
||||
labelAccessor: 'currency_code',
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} currencies
|
||||
* @returns
|
||||
*/
|
||||
export function CurrencySelect({ currencies, ...rest }) {
|
||||
return (
|
||||
<FSelect
|
||||
{...currencySelectProps}
|
||||
{...rest}
|
||||
items={currencies}
|
||||
input={CurrnecySelectButton}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {*} label
|
||||
* @returns
|
||||
*/
|
||||
function CurrnecySelectButton({ label }) {
|
||||
return <Button text={label ? label : intl.get('select_currency_code')} />;
|
||||
}
|
||||
2
src/components/Currencies/index.js
Normal file
2
src/components/Currencies/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './CurrencySelect';
|
||||
export * from './BaseCurrency';
|
||||
109
src/components/CustomSelectList.js
Normal file
109
src/components/CustomSelectList.js
Normal file
@@ -0,0 +1,109 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {
|
||||
Button,
|
||||
MenuItem,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
Classes,
|
||||
} from '@blueprintjs/core';
|
||||
import clsx from 'classnames';
|
||||
import { defaultTo } from 'lodash';
|
||||
import { Select } from '@blueprintjs/select';
|
||||
import { FormattedMessage as T, Icon } from 'components';
|
||||
|
||||
/**
|
||||
* warehouse & branches select list.
|
||||
* @returns
|
||||
*/
|
||||
export default function CustomSelectList({
|
||||
// #ownProps
|
||||
items,
|
||||
initialItemId,
|
||||
selectedItemId,
|
||||
loading = false,
|
||||
defaultSelectText,
|
||||
onItemSelected,
|
||||
buttonProps,
|
||||
}) {
|
||||
const initialItem = React.useMemo(
|
||||
() => items.find((a) => a.id === initialItemId),
|
||||
[initialItemId, items],
|
||||
);
|
||||
|
||||
const [selecetedItem, setSelectedItem] = React.useState(initialItem || null);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof selectedItemId !== 'undefined') {
|
||||
const item = selectedItemId
|
||||
? items.find((a) => a.id === selectedItemId)
|
||||
: null;
|
||||
setSelectedItem(item);
|
||||
}
|
||||
}, [selectedItemId, items, setSelectedItem]);
|
||||
|
||||
// Menu items renderer.
|
||||
const itemRenderer = (item, { handleClick, modifiers, query }) => (
|
||||
<MenuItem
|
||||
text={item.name}
|
||||
key={item.id}
|
||||
label={item.code}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
|
||||
// Filters items items.
|
||||
const filterItemsPredicater = (query, item, _index, exactMatch) => {
|
||||
const normalizedTitle = item.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return `${item.code} ${normalizedTitle}`.indexOf(normalizedQuery) >= 0;
|
||||
}
|
||||
};
|
||||
|
||||
const handleItemMenuSelect = React.useCallback(
|
||||
(item) => {
|
||||
if (item.id) {
|
||||
setSelectedItem({ ...item });
|
||||
onItemSelected && onItemSelected(item);
|
||||
}
|
||||
},
|
||||
[onItemSelected, setSelectedItem],
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
items={items}
|
||||
noResults={<MenuItem disabled={true} text={<T id={'no_accounts'} />} />}
|
||||
itemRenderer={itemRenderer}
|
||||
itemPredicate={filterItemsPredicater}
|
||||
onItemSelect={handleItemMenuSelect}
|
||||
popoverProps={{
|
||||
minimal: true,
|
||||
position: Position.BOTTOM_LEFT,
|
||||
interactionKind: PopoverInteractionKind.CLICK,
|
||||
modifiers: {
|
||||
offset: { offset: '0, 4' },
|
||||
},
|
||||
}}
|
||||
className={clsx({ [Classes.SKELETON]: loading })}
|
||||
>
|
||||
<Button
|
||||
text={
|
||||
selecetedItem
|
||||
? `${defaultSelectText}:${selecetedItem.name} ${defaultTo(
|
||||
selecetedItem.code,
|
||||
'',
|
||||
)}`
|
||||
: `${defaultSelectText}: Bigcapital`
|
||||
}
|
||||
minimal={true}
|
||||
small={true}
|
||||
{...buttonProps}
|
||||
/>
|
||||
</Select>
|
||||
);
|
||||
}
|
||||
@@ -8,16 +8,22 @@ function CustomerDrawerLinkComponent({
|
||||
// #ownProps
|
||||
children,
|
||||
customerId,
|
||||
className,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
// Handle view customer drawer.
|
||||
const handleCustomerDrawer = () => {
|
||||
const handleCustomerDrawer = (event) => {
|
||||
openDrawer('customer-details-drawer', { customerId });
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
return <ButtonLink onClick={handleCustomerDrawer}>{children}</ButtonLink>;
|
||||
return (
|
||||
<ButtonLink className={className} onClick={handleCustomerDrawer}>
|
||||
{children}
|
||||
</ButtonLink>
|
||||
);
|
||||
}
|
||||
|
||||
export const CustomerDrawerLink = R.compose(withDrawerActions)(
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React, { useRef, useCallback, useMemo } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { useCellAutoFocus } from 'hooks';
|
||||
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { CellType } from 'common';
|
||||
import { useCellAutoFocus } from 'hooks';
|
||||
import AccountsSuggestField from 'components/AccountsSuggestField';
|
||||
|
||||
// import AccountsSelectList from 'components/AccountsSelectList';
|
||||
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
|
||||
|
||||
/**
|
||||
* Account cell renderer.
|
||||
*/
|
||||
@@ -74,3 +73,4 @@ export default function AccountCellRenderer({
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
AccountCellRenderer.cellType = CellType.Field;
|
||||
|
||||
44
src/components/DataTableCells/BranchesListFieldCell.js
Normal file
44
src/components/DataTableCells/BranchesListFieldCell.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import { FormGroup, Intent, Classes } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CellType } from 'common';
|
||||
import BranchSuggestField from '../BranchSuggestField';
|
||||
|
||||
/**
|
||||
* Branches list field cell.
|
||||
* @returns
|
||||
*/
|
||||
export default function BranchesListFieldCell({
|
||||
column: { id },
|
||||
row: { index, original },
|
||||
payload: { branches, updateData, errors },
|
||||
}) {
|
||||
const handleBranchSelected = React.useCallback(
|
||||
(branch) => {
|
||||
updateData(index, 'branch_id', branch.id);
|
||||
},
|
||||
[updateData, index],
|
||||
);
|
||||
|
||||
const error = errors?.[index]?.[id];
|
||||
|
||||
return (
|
||||
<FormGroup
|
||||
intent={error ? Intent.DANGER : null}
|
||||
className={classNames(
|
||||
'form-group--select-list',
|
||||
'form-group--contacts-list',
|
||||
Classes.FILL,
|
||||
)}
|
||||
>
|
||||
<BranchSuggestField
|
||||
branches={branches}
|
||||
onBranchSelected={handleBranchSelected}
|
||||
selectedBranchId={original?.branch_id}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
BranchesListFieldCell.cellType = CellType.Field;
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { get } from 'lodash';
|
||||
import { Classes, Checkbox, FormGroup, Intent } from '@blueprintjs/core';
|
||||
import { CellType } from 'common';
|
||||
|
||||
const CheckboxEditableCell = ({
|
||||
row: { index, original },
|
||||
@@ -45,4 +46,6 @@ const CheckboxEditableCell = ({
|
||||
);
|
||||
};
|
||||
|
||||
CheckboxEditableCell.cellType = CellType.Field;
|
||||
|
||||
export default CheckboxEditableCell;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import { FormGroup, Intent, Classes } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
import { ContactSelecetList } from 'components';
|
||||
import ContactsSuggestField from 'components/ContactsSuggestField';
|
||||
|
||||
import { CellType } from 'common';
|
||||
import ContactsSuggestField from 'components/ContactsSuggestField';
|
||||
export default function ContactsListCellRenderer({
|
||||
column: { id },
|
||||
row: { index, original },
|
||||
@@ -37,3 +37,5 @@ export default function ContactsListCellRenderer({
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
ContactsListCellRenderer.cellType = CellType.Field;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Classes, InputGroup, FormGroup, Intent } from '@blueprintjs/core';
|
||||
import { CellType } from 'common';
|
||||
|
||||
const InputEditableCell = ({
|
||||
row: { index },
|
||||
@@ -37,4 +38,6 @@ const InputEditableCell = ({
|
||||
);
|
||||
};
|
||||
|
||||
InputEditableCell.cellType = CellType.Field;
|
||||
|
||||
export default InputEditableCell;
|
||||
|
||||
@@ -3,6 +3,7 @@ import classNames from 'classnames';
|
||||
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
|
||||
import intl from 'react-intl-universal';
|
||||
|
||||
import { CellType } from 'common';
|
||||
import ItemsSuggestField from 'components/ItemsSuggestField';
|
||||
|
||||
import { useCellAutoFocus } from 'hooks';
|
||||
@@ -54,3 +55,5 @@ export default function ItemsListCell({
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
ItemsListCell.cellType = CellType.Field;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import React, { useCallback, useState, useEffect } from 'react';
|
||||
import { FormGroup, Intent } from '@blueprintjs/core';
|
||||
|
||||
import { MoneyInputGroup } from 'components';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { CellType } from 'common';
|
||||
|
||||
// Input form cell renderer.
|
||||
const MoneyFieldCellRenderer = ({
|
||||
@@ -48,4 +50,6 @@ const MoneyFieldCellRenderer = ({
|
||||
);
|
||||
};
|
||||
|
||||
MoneyFieldCellRenderer.cellType = CellType.Field;
|
||||
|
||||
export default MoneyFieldCellRenderer;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { FormGroup, NumericInput, Intent } from '@blueprintjs/core';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { CellType } from 'common';
|
||||
import { CLASSES } from 'common/classes';
|
||||
|
||||
/**
|
||||
@@ -36,8 +38,10 @@ export default function NumericInputCell({
|
||||
onValueChange={handleValueChange}
|
||||
onBlur={onBlur}
|
||||
fill={true}
|
||||
buttonPosition={"none"}
|
||||
buttonPosition={'none'}
|
||||
/>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
NumericInputCell.cellType = CellType.Field;
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import PaymentReceiveListField from 'components/PaymentReceiveListField';
|
||||
import classNames from 'classnames';
|
||||
import { FormGroup, Classes, Intent } from '@blueprintjs/core';
|
||||
|
||||
import PaymentReceiveListField from 'components/PaymentReceiveListField';
|
||||
import { CellType } from 'common';
|
||||
function PaymentReceiveListFieldCell({
|
||||
column: { id },
|
||||
row: { index },
|
||||
@@ -32,4 +33,6 @@ function PaymentReceiveListFieldCell({
|
||||
);
|
||||
}
|
||||
|
||||
PaymentReceiveListFieldCell.cellType = CellType.Field;
|
||||
|
||||
export default PaymentReceiveListFieldCell;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useCallback, useState, useEffect } from 'react';
|
||||
import { FormGroup, Intent } from '@blueprintjs/core';
|
||||
|
||||
import { MoneyInputGroup } from 'components';
|
||||
import { CellType } from 'common';
|
||||
|
||||
const PercentFieldCell = ({
|
||||
cell: { value: initialValue },
|
||||
@@ -38,4 +40,6 @@ const PercentFieldCell = ({
|
||||
);
|
||||
};
|
||||
|
||||
PercentFieldCell.cellType = CellType.Field;
|
||||
|
||||
export default PercentFieldCell;
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Classes, Switch, FormGroup, Intent } from '@blueprintjs/core';
|
||||
|
||||
import { CellType } from 'common';
|
||||
import { safeInvoke } from 'utils';
|
||||
|
||||
/**
|
||||
@@ -48,4 +49,6 @@ const SwitchEditableCell = ({
|
||||
);
|
||||
};
|
||||
|
||||
export default SwitchEditableCell;
|
||||
SwitchEditableCell.cellType = CellType.Field;
|
||||
|
||||
export default SwitchEditableCell;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Classes, TextArea, FormGroup, Intent } from '@blueprintjs/core';
|
||||
import { CellType } from 'common';
|
||||
|
||||
const TextAreaEditableCell = ({
|
||||
row: { index },
|
||||
@@ -39,4 +40,6 @@ const TextAreaEditableCell = ({
|
||||
);
|
||||
};
|
||||
|
||||
TextAreaEditableCell.cellType = CellType.Field;
|
||||
|
||||
export default TextAreaEditableCell;
|
||||
|
||||
@@ -9,6 +9,7 @@ import NumericInputCell from './NumericInputCell';
|
||||
import CheckBoxFieldCell from './CheckBoxFieldCell';
|
||||
import SwitchFieldCell from './SwitchFieldCell';
|
||||
import TextAreaCell from './TextAreaCell';
|
||||
import BranchesListFieldCell from './BranchesListFieldCell';
|
||||
|
||||
export {
|
||||
AccountsListFieldCell,
|
||||
@@ -23,4 +24,5 @@ export {
|
||||
CheckBoxFieldCell,
|
||||
SwitchFieldCell,
|
||||
TextAreaCell,
|
||||
BranchesListFieldCell,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { CLASSES } from 'common/classes';
|
||||
import { DataTable, If } from 'components';
|
||||
import 'style/components/DataTable/DataTableEditable.scss';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { DataTable } from 'components';
|
||||
|
||||
/**
|
||||
* Editable datatable.
|
||||
@@ -11,26 +10,106 @@ export default function DatatableEditable({
|
||||
totalRow = false,
|
||||
actions,
|
||||
name,
|
||||
className,
|
||||
...tableProps
|
||||
}) {
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
CLASSES.DATATABLE_EDITOR,
|
||||
{
|
||||
[`${CLASSES.DATATABLE_EDITOR}--${name}`]: name,
|
||||
},
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<DatatableEditableRoot>
|
||||
<DataTable {...tableProps} />
|
||||
|
||||
<If condition={actions}>
|
||||
<div className={classNames(CLASSES.DATATABLE_EDITOR_ACTIONS)}>
|
||||
{actions}
|
||||
</div>
|
||||
</If>
|
||||
</div>
|
||||
</DatatableEditableRoot>
|
||||
);
|
||||
}
|
||||
|
||||
const DatatableEditableRoot = styled.div`
|
||||
.bp3-form-group {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.table {
|
||||
border: 1px solid #d2dce2;
|
||||
border-radius: 5px;
|
||||
background-color: #fff;
|
||||
|
||||
.th,
|
||||
.td {
|
||||
border-left: 1px solid #e2e2e2;
|
||||
|
||||
&:first-of-type{
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.thead {
|
||||
.tr .th {
|
||||
padding: 9px 14px;
|
||||
background-color: #f2f3fb;
|
||||
font-size: 13px;
|
||||
color: #415060;
|
||||
border-bottom: 1px solid #d2dce2;
|
||||
|
||||
&,
|
||||
.inner-resizer {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
.tbody {
|
||||
.tr .td {
|
||||
border-bottom: 0;
|
||||
border-bottom: 1px solid #d8d8d8;
|
||||
min-height: 38px;
|
||||
padding: 4px 14px;
|
||||
|
||||
&.td-field-type,
|
||||
&.td-button-type{
|
||||
padding: 2px;
|
||||
}
|
||||
}
|
||||
.tr:last-of-type .td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.tr {
|
||||
&:hover .td,
|
||||
.bp3-input {
|
||||
background-color: transparent;
|
||||
}
|
||||
.bp3-form-group:not(.bp3-intent-danger) .bp3-input,
|
||||
.form-group--select-list .bp3-button {
|
||||
border-color: #ffffff;
|
||||
color: #222;
|
||||
border-radius: 3px;
|
||||
text-align: inherit;
|
||||
}
|
||||
.bp3-form-group:not(.bp3-intent-danger) .bp3-input {
|
||||
border-radius: 2px;
|
||||
padding-left: 14px;
|
||||
padding-right: 14px;
|
||||
|
||||
&:focus {
|
||||
box-shadow: 0 0 0 2px #116cd0;
|
||||
}
|
||||
}
|
||||
.form-group--select-list .bp3-button {
|
||||
padding-left: 6px;
|
||||
padding-right: 6px;
|
||||
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.form-group--select-list,
|
||||
.bp3-form-group {
|
||||
&.bp3-intent-danger {
|
||||
.bp3-button:not(.bp3-minimal),
|
||||
.bp3-input {
|
||||
border-color: #f7b6b6;
|
||||
}
|
||||
}
|
||||
}
|
||||
.td.actions {
|
||||
.bp3-button {
|
||||
color: #80858f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import React, { useContext } from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { If } from 'components';
|
||||
import { Skeleton } from 'components';
|
||||
import { camelCase} from 'lodash';
|
||||
|
||||
import { If, Skeleton } from 'components';
|
||||
import { useAppIntlContext } from 'components/AppIntlProvider';
|
||||
import TableContext from './TableContext';
|
||||
import { saveInvoke, ignoreEventFromSelectors } from 'utils';
|
||||
@@ -56,7 +57,8 @@ export default function TableCell({ cell, row, index }) {
|
||||
return;
|
||||
}
|
||||
saveInvoke(onCellClick, cell, event);
|
||||
};
|
||||
};
|
||||
const cellType = camelCase(cell.column.Cell.cellType) || 'text';
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -65,6 +67,9 @@ export default function TableCell({ cell, row, index }) {
|
||||
'is-text-overview': cell.column.textOverview,
|
||||
clickable: cell.column.clickable,
|
||||
'align-right': cell.column.align === 'right',
|
||||
'align-center': cell.column.align === 'center',
|
||||
[`td-${cell.column.id}`]: cell.column.id,
|
||||
[`td-${cellType}-type`]: !!cellType,
|
||||
}),
|
||||
onClick: handleCellClick,
|
||||
})}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Checkbox } from '@blueprintjs/core';
|
||||
|
||||
import { CellType } from 'common';
|
||||
export default function TableIndeterminateCheckboxRow({ row }) {
|
||||
return (
|
||||
<div class="selection-checkbox">
|
||||
@@ -8,3 +8,5 @@ export default function TableIndeterminateCheckboxRow({ row }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
TableIndeterminateCheckboxRow.cellType = CellType.Field;
|
||||
|
||||
34
src/components/DetailExchangeRate.js
Normal file
34
src/components/DetailExchangeRate.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import * as R from 'ramda';
|
||||
import { DetailItem } from 'components';
|
||||
import { isEqual } from 'lodash';
|
||||
|
||||
import withCurrentOrganization from 'containers/Organization/withCurrentOrganization';
|
||||
|
||||
/**
|
||||
* Detail exchange rate item.
|
||||
* @param {*} param0
|
||||
* @param {*} param1
|
||||
* @returns
|
||||
*/
|
||||
function DetailExchangeRate({
|
||||
exchangeRate,
|
||||
toCurrency,
|
||||
// #withCurrentOrganization
|
||||
organization: { base_currency },
|
||||
}) {
|
||||
if (isEqual(base_currency, toCurrency)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<DetailItem label={intl.get('exchange_rate')}>
|
||||
1 {base_currency} = {exchangeRate} {toCurrency}
|
||||
</DetailItem>
|
||||
);
|
||||
}
|
||||
|
||||
export const ExchangeRateDetailItem = R.compose(withCurrentOrganization())(
|
||||
DetailExchangeRate,
|
||||
);
|
||||
@@ -34,6 +34,12 @@ import UnlockingTransactionsDialog from '../containers/Dialogs/UnlockingTransact
|
||||
import UnlockingPartialTransactionsDialog from '../containers/Dialogs/UnlockingPartialTransactionsDialog';
|
||||
import CreditNotePdfPreviewDialog from '../containers/Dialogs/CreditNotePdfPreviewDialog';
|
||||
import PaymentReceivePdfPreviewDialog from '../containers/Dialogs/PaymentReceivePdfPreviewDialog';
|
||||
import WarehouseFormDialog from '../containers/Dialogs/WarehouseFormDialog';
|
||||
import BranchFormDialog from '../containers/Dialogs/BranchFormDialog';
|
||||
import BranchActivateDialog from '../containers/Dialogs/BranchActivateDialog';
|
||||
import WarehouseActivateDialog from '../containers/Dialogs/WarehouseActivateDialog';
|
||||
import CustomerOpeningBalanceDialog from '../containers/Dialogs/CustomerOpeningBalanceDialog';
|
||||
import VendorOpeningBalanceDialog from '../containers/Dialogs/VendorOpeningBalanceDialog';
|
||||
|
||||
/**
|
||||
* Dialogs container.
|
||||
@@ -78,6 +84,12 @@ export default function DialogsContainer() {
|
||||
/>
|
||||
<CreditNotePdfPreviewDialog dialogName={'credit-note-pdf-preview'} />
|
||||
<PaymentReceivePdfPreviewDialog dialogName={'payment-pdf-preview'} />
|
||||
<WarehouseFormDialog dialogName={'warehouse-form'} />
|
||||
<BranchFormDialog dialogName={'branch-form'} />
|
||||
<BranchActivateDialog dialogName={'branch-activate'} />
|
||||
<WarehouseActivateDialog dialogName={'warehouse-activate'} />
|
||||
<CustomerOpeningBalanceDialog dialogName={'customer-opening-balance'} />
|
||||
<VendorOpeningBalanceDialog dialogName={'vendor-opening-balance'} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Classes, Icon, H4, Button } from '@blueprintjs/core';
|
||||
|
||||
import withDrawerActions from 'containers/Drawer/withDrawerActions';
|
||||
|
||||
import styled from 'styled-components';
|
||||
import { compose } from 'utils';
|
||||
|
||||
/**
|
||||
@@ -13,6 +14,7 @@ function DrawerHeaderContent(props) {
|
||||
const {
|
||||
icon,
|
||||
title = <T id={'view_paper'} />,
|
||||
subTitle,
|
||||
onClose,
|
||||
name,
|
||||
closeDrawer,
|
||||
@@ -30,7 +32,10 @@ function DrawerHeaderContent(props) {
|
||||
return (
|
||||
<div className={Classes.DRAWER_HEADER}>
|
||||
<Icon icon={icon} iconSize={Icon.SIZE_LARGE} />
|
||||
<H4>{title}</H4>
|
||||
<H4>
|
||||
{title}
|
||||
<SubTitle>{subTitle}</SubTitle>
|
||||
</H4>
|
||||
|
||||
<Button
|
||||
aria-label="Close"
|
||||
@@ -44,3 +49,24 @@ function DrawerHeaderContent(props) {
|
||||
}
|
||||
|
||||
export default compose(withDrawerActions)(DrawerHeaderContent);
|
||||
|
||||
/**
|
||||
* SubTitle Drawer header.
|
||||
* @returns {React.JSX}
|
||||
*/
|
||||
function SubTitle({ children }) {
|
||||
if (children == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <SubTitleHead>{children}</SubTitleHead>;
|
||||
}
|
||||
|
||||
const SubTitleHead = styled.div`
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 1;
|
||||
padding: 2px 0px;
|
||||
margin: 2px 0px;
|
||||
`;
|
||||
|
||||
@@ -21,6 +21,7 @@ import CreditNoteDetailDrawer from '../containers/Drawers/CreditNoteDetailDrawer
|
||||
import VendorCreditDetailDrawer from '../containers/Drawers/VendorCreditDetailDrawer';
|
||||
import RefundCreditNoteDetailDrawer from '../containers/Drawers/RefundCreditNoteDetailDrawer';
|
||||
import RefundVendorCreditDetailDrawer from '../containers/Drawers/RefundVendorCreditDetailDrawer';
|
||||
import WarehouseTransferDetailDrawer from '../containers/Drawers/WarehouseTransferDetailDrawer'
|
||||
|
||||
import { DRAWERS } from 'common/drawers';
|
||||
|
||||
@@ -59,6 +60,7 @@ export default function DrawersContainer() {
|
||||
<RefundVendorCreditDetailDrawer
|
||||
name={DRAWERS.REFUND_VENDOR_CREDIT_DETAIL_DRAWER}
|
||||
/>
|
||||
<WarehouseTransferDetailDrawer name={DRAWERS.WAREHOUSE_TRANSFER_DETAIL_DRAWER} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
58
src/components/ExchangeRate/ExchangeRateInput.js
Normal file
58
src/components/ExchangeRate/ExchangeRateInput.js
Normal file
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { ControlGroup } from '@blueprintjs/core';
|
||||
|
||||
import { FlagIcon } from '../Tags';
|
||||
import { FMoneyInputGroup, FFormGroup } from '../Forms';
|
||||
|
||||
export function ExchangeRateInputGroup({
|
||||
fromCurrency,
|
||||
toCurrency,
|
||||
inputGroupProps,
|
||||
formGroupProps,
|
||||
name,
|
||||
}) {
|
||||
return (
|
||||
<FFormGroup inline={true} {...formGroupProps} name={name}>
|
||||
<ControlGroup>
|
||||
<ExchangeRatePrepend>
|
||||
<ExchangeFlagIcon currencyCode={fromCurrency} /> 1 {fromCurrency} =
|
||||
</ExchangeRatePrepend>
|
||||
<ExchangeRateField
|
||||
allowDecimals={true}
|
||||
allowNegativeValue={true}
|
||||
{...inputGroupProps}
|
||||
name={name}
|
||||
/>
|
||||
<ExchangeRateAppend>
|
||||
<ExchangeFlagIcon currencyCode={toCurrency} /> {toCurrency}
|
||||
</ExchangeRateAppend>
|
||||
</ControlGroup>
|
||||
</FFormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const ExchangeRateField = styled(FMoneyInputGroup)`
|
||||
max-width: 75px;
|
||||
`;
|
||||
|
||||
const ExchangeRateSideIcon = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
line-height: 1;
|
||||
`;
|
||||
|
||||
const ExchangeRatePrepend = styled(ExchangeRateSideIcon)`
|
||||
padding-right: 8px;
|
||||
`;
|
||||
|
||||
const ExchangeRateAppend = styled(ExchangeRateSideIcon)`
|
||||
padding-left: 8px;
|
||||
`;
|
||||
|
||||
const ExchangeFlagIcon = styled(FlagIcon)`
|
||||
margin-right: 5px;
|
||||
margin-left: 5px;
|
||||
display: inline-block;
|
||||
`;
|
||||
94
src/components/ExchangeRate/ExchangeRateMutedField.js
Normal file
94
src/components/ExchangeRate/ExchangeRateMutedField.js
Normal file
@@ -0,0 +1,94 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import intl from 'react-intl-universal';
|
||||
import {
|
||||
Button,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
FormGroup,
|
||||
Position,
|
||||
Classes,
|
||||
} from '@blueprintjs/core';
|
||||
import { ExchangeRateInputGroup, Icon } from 'components';
|
||||
|
||||
export function ExchangeRateMutedField({
|
||||
name,
|
||||
toCurrency,
|
||||
fromCurrency,
|
||||
date,
|
||||
exchangeRate,
|
||||
exchangeRateFieldProps,
|
||||
popoverProps,
|
||||
}) {
|
||||
const content = (
|
||||
<ExchangeRateFormGroupContent>
|
||||
<ExchangeRateInputGroup
|
||||
name={name}
|
||||
fromCurrency={fromCurrency}
|
||||
toCurrency={toCurrency}
|
||||
{...exchangeRateFieldProps}
|
||||
/>
|
||||
</ExchangeRateFormGroupContent>
|
||||
);
|
||||
|
||||
return (
|
||||
<ExchangeRateFormGroup label={`As on ${date},`}>
|
||||
<Popover
|
||||
content={content}
|
||||
interactionKind={PopoverInteractionKind.CLICK}
|
||||
position={Position.RIGHT_TOP}
|
||||
modifiers={{
|
||||
offset: { offset: '0, 4' },
|
||||
}}
|
||||
{...popoverProps}
|
||||
minimal={true}
|
||||
usePortal={false}
|
||||
target={<div />}
|
||||
>
|
||||
<ExchangeRateButton>
|
||||
1 {fromCurrency} = {exchangeRate} {toCurrency}
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
rightIcon={<Icon icon="pen-18" iconSize={14} />}
|
||||
small={true}
|
||||
/>
|
||||
</ExchangeRateButton>
|
||||
</Popover>
|
||||
</ExchangeRateFormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const ExchangeRateFormGroup = styled(FormGroup)`
|
||||
&.bp3-form-group {
|
||||
label.bp3-label {
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
line-height: 1;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ExchangeRateButton = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
color: #0d244a;
|
||||
position: relative;
|
||||
padding-right: 28px;
|
||||
|
||||
.bp3-button {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ExchangeRateFormGroupContent = styled.div`
|
||||
padding: 5px 0;
|
||||
|
||||
.bp3-form-group {
|
||||
padding: 2px;
|
||||
margin: 2px 4px !important;
|
||||
}
|
||||
`;
|
||||
2
src/components/ExchangeRate/index.js
Normal file
2
src/components/ExchangeRate/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './ExchangeRateInput';
|
||||
export * from './ExchangeRateMutedField'
|
||||
13
src/components/FeatureGuard/FeatureCan.js
Normal file
13
src/components/FeatureGuard/FeatureCan.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import * as R from 'ramda';
|
||||
import withFeatureCan from './withFeatureCan';
|
||||
|
||||
function FeatureCanJSX({ feature, children, isFeatureCan }) {
|
||||
return isFeatureCan && children;
|
||||
}
|
||||
|
||||
export const FeatureCan = R.compose(
|
||||
withFeatureCan(({ isFeatureCan }) => ({
|
||||
isFeatureCan,
|
||||
})),
|
||||
)(FeatureCanJSX);
|
||||
1
src/components/FeatureGuard/index.js
Normal file
1
src/components/FeatureGuard/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './FeatureCan';
|
||||
17
src/components/FeatureGuard/withFeatureCan.js
Normal file
17
src/components/FeatureGuard/withFeatureCan.js
Normal file
@@ -0,0 +1,17 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { getDashboardFeaturesSelector } from '../../store/dashboard/dashboard.selectors';
|
||||
|
||||
export default (mapState) => {
|
||||
const featuresSelector = getDashboardFeaturesSelector();
|
||||
|
||||
const mapStateToProps = (state, props) => {
|
||||
const features = featuresSelector(state);
|
||||
|
||||
const mapped = {
|
||||
isFeatureCan: !!features[props.feature],
|
||||
features,
|
||||
};
|
||||
return mapState ? mapState(mapped, state, props) : mapped;
|
||||
};
|
||||
return connect(mapStateToProps);
|
||||
};
|
||||
44
src/components/FormTopbar.js
Normal file
44
src/components/FormTopbar.js
Normal file
@@ -0,0 +1,44 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { Navbar } from '@blueprintjs/core';
|
||||
|
||||
/**
|
||||
* Form the topbar.
|
||||
* @param {JSX.Element} children
|
||||
* @returns {JSX.Element}
|
||||
*/
|
||||
export function FormTopbar({ className, children }) {
|
||||
return <FormTopBarRoot className={className}>{children}</FormTopBarRoot>;
|
||||
}
|
||||
|
||||
const FormTopBarRoot = styled(Navbar)`
|
||||
box-shadow: 0 0 0;
|
||||
border-bottom: 1px solid #c7d5db;
|
||||
height: 35px;
|
||||
padding: 0 20px;
|
||||
|
||||
.bp3-navbar-group {
|
||||
height: 35px;
|
||||
}
|
||||
.bp3-navbar-divider {
|
||||
border-left-color: #d2dce2;
|
||||
}
|
||||
.bp3-skeleton {
|
||||
max-height: 10px;
|
||||
}
|
||||
.bp3-button {
|
||||
&:hover {
|
||||
background: rgba(167, 182, 194, 0.12);
|
||||
color: #32304a;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const DetailsBarSkeletonBase = styled.div`
|
||||
letter-spacing: 10px;
|
||||
margin-right: 10px;
|
||||
margin-left: 10px;
|
||||
font-size: 8px;
|
||||
width: 140px;
|
||||
height: 10px;
|
||||
`;
|
||||
24
src/components/Forms/BlueprintFormik.js
Normal file
24
src/components/Forms/BlueprintFormik.js
Normal file
@@ -0,0 +1,24 @@
|
||||
import {
|
||||
FormGroup,
|
||||
InputGroup,
|
||||
NumericInput,
|
||||
Checkbox,
|
||||
RadioGroup,
|
||||
Switch,
|
||||
EditableText,
|
||||
TextArea,
|
||||
} from '@blueprintjs-formik/core';
|
||||
import { Select, MultiSelect } from '@blueprintjs-formik/select';
|
||||
|
||||
export {
|
||||
FormGroup as FFormGroup,
|
||||
InputGroup as FInputGroup,
|
||||
NumericInput as FNumericInput,
|
||||
Checkbox as FCheckbox,
|
||||
RadioGroup as FRadioGroup,
|
||||
Switch as FSwitch,
|
||||
Select as FSelect,
|
||||
MultiSelect as FMultiSelect,
|
||||
EditableText as FEditableText,
|
||||
TextArea as FTextArea,
|
||||
};
|
||||
36
src/components/Forms/FMoneyInputGroup.js
Normal file
36
src/components/Forms/FMoneyInputGroup.js
Normal file
@@ -0,0 +1,36 @@
|
||||
import React from 'react';
|
||||
import { Intent } from '@blueprintjs/core';
|
||||
import { Field, getIn } from 'formik';
|
||||
import { CurrencyInput } from './MoneyInputGroup';
|
||||
|
||||
const fieldToMoneyInputGroup = ({
|
||||
field: { onBlur: onFieldBlur, ...field },
|
||||
form: { setFieldValue, touched, errors },
|
||||
onBlur,
|
||||
...props
|
||||
}) => {
|
||||
const fieldError = getIn(errors, field.name);
|
||||
const showError = getIn(touched, field.name) && !!fieldError;
|
||||
|
||||
return {
|
||||
intent: showError ? Intent.DANGER : Intent.NONE,
|
||||
onBlurValue:
|
||||
onBlur ??
|
||||
function (e) {
|
||||
onFieldBlur(e ?? field.name);
|
||||
},
|
||||
...field,
|
||||
onChange: (value) => {
|
||||
setFieldValue(field.name, value);
|
||||
},
|
||||
...props,
|
||||
};
|
||||
};
|
||||
|
||||
function FieldToMoneyInputGroup({ ...props }) {
|
||||
return <CurrencyInput {...fieldToMoneyInputGroup(props)} />;
|
||||
}
|
||||
|
||||
export function FMoneyInputGroup({ ...props }) {
|
||||
return <Field {...props} component={FieldToMoneyInputGroup} />;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
|
||||
import { useFormikContext } from 'formik';
|
||||
import { useDeepCompareEffect } from 'hooks/utils';
|
||||
|
||||
export function FormikObserver({ onChange, values }) {
|
||||
export function FormikObserver({ onChange }) {
|
||||
const { values } = useFormikContext();
|
||||
|
||||
useDeepCompareEffect(() => {
|
||||
onChange(values);
|
||||
}, [values]);
|
||||
|
||||
@@ -1,2 +1,4 @@
|
||||
export * from './FormObserver';
|
||||
export * from './FormikObserver';
|
||||
export * from './FormikObserver';
|
||||
export * from './FMoneyInputGroup'
|
||||
export * from './BlueprintFormik';
|
||||
12
src/components/Paper/Paper.js
Normal file
12
src/components/Paper/Paper.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export function Paper({ children, className }) {
|
||||
return <PaperRoot className={className}>{children}</PaperRoot>;
|
||||
}
|
||||
|
||||
const PaperRoot = styled.div`
|
||||
border: 1px solid #d2dce2;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
`;
|
||||
1
src/components/Paper/index.js
Normal file
1
src/components/Paper/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Paper';
|
||||
@@ -6,6 +6,8 @@ import { CLASSES } from 'common/classes';
|
||||
import DashboardTopbarUser from 'components/Dashboard/TopbarUser';
|
||||
import UsersActions from 'containers/Preferences/Users/UsersActions';
|
||||
import CurrenciesActions from 'containers/Preferences/Currencies/CurrenciesActions';
|
||||
import WarehousesActions from '../../containers/Preferences/Warehouses/WarehousesActions';
|
||||
import BranchesActions from '../../containers/Preferences/Branches/BranchesActions';
|
||||
import withDashboard from 'containers/Dashboard/withDashboard';
|
||||
|
||||
import { compose } from 'utils';
|
||||
@@ -35,6 +37,16 @@ function PreferencesTopbar({ preferencesPageTitle }) {
|
||||
path={'/preferences/currencies'}
|
||||
component={CurrenciesActions}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={'/preferences/warehouses'}
|
||||
component={WarehousesActions}
|
||||
/>
|
||||
<Route
|
||||
exact
|
||||
path={'/preferences/branches'}
|
||||
component={BranchesActions}
|
||||
/>
|
||||
</Switch>
|
||||
</Route>
|
||||
</div>
|
||||
|
||||
@@ -9,3 +9,13 @@ export const CurrencyTag = styled.span`
|
||||
line-height: 1;
|
||||
margin-left: 4px;
|
||||
`;
|
||||
|
||||
export const BaseCurrencyRoot = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 10px;
|
||||
margin-left: 4px;
|
||||
> span {
|
||||
background: #5c7080;
|
||||
}
|
||||
`;
|
||||
|
||||
7
src/components/Tags/FlagIcon.js
Normal file
7
src/components/Tags/FlagIcon.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
export const FlagIcon = ({ currencyCode, className }) => {
|
||||
const source = `/icons/flags/${currencyCode}.svg`;
|
||||
|
||||
return <img alt="flag" src={source} className={className} />;
|
||||
};
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
|
||||
export * from './CurrencyTag';
|
||||
export * from './CurrencyTag';
|
||||
export * from './FlagIcon'
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const TotalLineBorderStyle = {
|
||||
None: 'None',
|
||||
SingleDark: 'SingleDark',
|
||||
DoubleDark: 'DoubleDark',
|
||||
};
|
||||
@@ -80,6 +81,11 @@ export const TotalLineRoot = styled.div`
|
||||
`
|
||||
border-bottom: 1px double #000;
|
||||
`}
|
||||
${(props) =>
|
||||
props.borderStyle === TotalLineBorderStyle.None &&
|
||||
`
|
||||
border-bottom-color: transparent;
|
||||
`}
|
||||
${(props) =>
|
||||
props.textStyle === TotalLineTextStyle.Bold &&
|
||||
`
|
||||
|
||||
@@ -8,16 +8,18 @@ function VendorDrawerLinkComponent({
|
||||
// #ownProps
|
||||
children,
|
||||
vendorId,
|
||||
className,
|
||||
|
||||
// #withDrawerActions
|
||||
openDrawer,
|
||||
}) {
|
||||
// Handle view customer drawer.
|
||||
const handleVendorDrawer = () => {
|
||||
const handleVendorDrawer = (event) => {
|
||||
openDrawer('vendor-details-drawer', { vendorId });
|
||||
event.preventDefault();
|
||||
};
|
||||
|
||||
return <ButtonLink onClick={handleVendorDrawer}>{children}</ButtonLink>;
|
||||
return <ButtonLink className={className} onClick={handleVendorDrawer}>{children}</ButtonLink>;
|
||||
}
|
||||
|
||||
export const VendorDrawerLink = R.compose(withDrawerActions)(VendorDrawerLinkComponent);
|
||||
|
||||
74
src/components/Warehouses/WarehouseMultiSelect.js
Normal file
74
src/components/Warehouses/WarehouseMultiSelect.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { MenuItem } from '@blueprintjs/core';
|
||||
import { FMultiSelect } from '../Forms';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} query
|
||||
* @param {*} warehouse
|
||||
* @param {*} _index
|
||||
* @param {*} exactMatch
|
||||
* @returns
|
||||
*/
|
||||
const warehouseItemPredicate = (query, warehouse, _index, exactMatch) => {
|
||||
const normalizedTitle = warehouse.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return (
|
||||
`${warehouse.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} branch
|
||||
* @param {*} param1
|
||||
* @returns
|
||||
*/
|
||||
const warehouseItemRenderer = (
|
||||
warehouse,
|
||||
{ handleClick, modifiers, query },
|
||||
{ isSelected },
|
||||
) => {
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
icon={isSelected ? 'tick' : 'blank'}
|
||||
text={warehouse.name}
|
||||
label={warehouse.code}
|
||||
key={warehouse.id}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const warehouseSelectProps = {
|
||||
itemPredicate: warehouseItemPredicate,
|
||||
itemRenderer: warehouseItemRenderer,
|
||||
valueAccessor: (item) => item.id,
|
||||
labelAccessor: (item) => item.code,
|
||||
tagRenderer: (item) => item.name,
|
||||
};
|
||||
|
||||
/**
|
||||
* warehouses mulit select.
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export function WarehouseMultiSelect({ warehouses, ...rest }) {
|
||||
return (
|
||||
<FMultiSelect
|
||||
items={warehouses}
|
||||
placeholder={intl.get('warehouses_multi_select.placeholder')}
|
||||
popoverProps={{ minimal: true, usePortal: false }}
|
||||
{...warehouseSelectProps}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
74
src/components/Warehouses/WarehouseSelect.js
Normal file
74
src/components/Warehouses/WarehouseSelect.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import React from 'react';
|
||||
|
||||
import { MenuItem, Button } from '@blueprintjs/core';
|
||||
import { FSelect } from '../Forms';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} query
|
||||
* @param {*} warehouse
|
||||
* @param {*} _index
|
||||
* @param {*} exactMatch
|
||||
* @returns
|
||||
*/
|
||||
const warehouseItemPredicate = (query, warehouse, _index, exactMatch) => {
|
||||
const normalizedTitle = warehouse.name.toLowerCase();
|
||||
const normalizedQuery = query.toLowerCase();
|
||||
|
||||
if (exactMatch) {
|
||||
return normalizedTitle === normalizedQuery;
|
||||
} else {
|
||||
return (
|
||||
`${warehouse.code}. ${normalizedTitle}`.indexOf(normalizedQuery) >= 0
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} film
|
||||
* @param {*} param1
|
||||
* @returns
|
||||
*/
|
||||
const warehouseItemRenderer = (
|
||||
warehouse,
|
||||
{ handleClick, modifiers, query },
|
||||
) => {
|
||||
const text = `${warehouse.name}`;
|
||||
|
||||
return (
|
||||
<MenuItem
|
||||
active={modifiers.active}
|
||||
disabled={modifiers.disabled}
|
||||
label={warehouse.code}
|
||||
key={warehouse.id}
|
||||
onClick={handleClick}
|
||||
text={text}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const warehouseSelectProps = {
|
||||
itemPredicate: warehouseItemPredicate,
|
||||
itemRenderer: warehouseItemRenderer,
|
||||
valueAccessor: 'id',
|
||||
labelAccessor: 'name',
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export function WarehouseSelect({ warehouses, ...rest }) {
|
||||
return <FSelect {...warehouseSelectProps} {...rest} items={warehouses} />;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} param0
|
||||
* @returns
|
||||
*/
|
||||
export function WarehouseSelectButton({ label, ...rest }) {
|
||||
return <Button text={label} />;
|
||||
}
|
||||
2
src/components/Warehouses/index.js
Normal file
2
src/components/Warehouses/index.js
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from './WarehouseSelect';
|
||||
export * from './WarehouseMultiSelect';
|
||||
@@ -54,6 +54,8 @@ import AvaterCell from './AvaterCell';
|
||||
|
||||
import { ItemsMultiSelect } from './Items';
|
||||
import MoreMenuItems from './MoreMenutItems';
|
||||
import CustomSelectList from './CustomSelectList';
|
||||
import { ExchangeRateDetailItem } from './DetailExchangeRate';
|
||||
|
||||
export * from './Dialog';
|
||||
export * from './Menu';
|
||||
@@ -88,13 +90,20 @@ export * from './TextStatus';
|
||||
export * from './Tags';
|
||||
export * from './CommercialDoc';
|
||||
export * from './Card';
|
||||
export * from './Customers'
|
||||
export * from './Vendors'
|
||||
export * from './Customers';
|
||||
export * from './Vendors';
|
||||
export * from './Table';
|
||||
export * from './Skeleton';
|
||||
export * from './FinancialStatement';
|
||||
export * from './FinancialReport';
|
||||
export * from './FinancialSheet';
|
||||
export * from './FeatureGuard';
|
||||
export * from './ExchangeRate';
|
||||
export * from './Branches';
|
||||
export * from './Warehouses';
|
||||
export * from './Currencies';
|
||||
export * from './FormTopbar'
|
||||
export * from './Paper';
|
||||
|
||||
const Hint = FieldHint;
|
||||
|
||||
@@ -161,4 +170,6 @@ export {
|
||||
ItemsMultiSelect,
|
||||
AvaterCell,
|
||||
MoreMenuItems,
|
||||
CustomSelectList,
|
||||
ExchangeRateDetailItem,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user