Merge branch 'develop' into main

This commit is contained in:
a.bouhuolia
2022-03-24 15:32:30 +02:00
757 changed files with 26203 additions and 1575 deletions

View 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);

View 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}
/>
);
}

View 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}
/>
);
}

View 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} />;
}

View File

@@ -0,0 +1,2 @@
export * from './BranchSelect';
export * from './BranchMultiSelect'

View File

@@ -0,0 +1,10 @@
import React from 'react';
import { CurrencyTag } from 'components';
/**
* base currecncy.
* @returns
*/
export function BaseCurrency({ currency }) {
return <CurrencyTag>{currency}</CurrencyTag>;
}

View 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')} />;
}

View File

@@ -0,0 +1,2 @@
export * from './CurrencySelect';
export * from './BaseCurrency';

View 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>
);
}

View File

@@ -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)(

View File

@@ -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;

View 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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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,
};

View File

@@ -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;
}
}
}
}
}
`;

View File

@@ -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,
})}

View File

@@ -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;

View 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,
);

View File

@@ -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>
);
}

View File

@@ -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;
`;

View File

@@ -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>
);
}

View 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;
`;

View 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;
}
`;

View File

@@ -0,0 +1,2 @@
export * from './ExchangeRateInput';
export * from './ExchangeRateMutedField'

View 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);

View File

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

View 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);
};

View 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;
`;

View 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,
};

View 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} />;
}

View File

@@ -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]);

View File

@@ -1,2 +1,4 @@
export * from './FormObserver';
export * from './FormikObserver';
export * from './FormikObserver';
export * from './FMoneyInputGroup'
export * from './BlueprintFormik';

View 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;
`;

View File

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

View File

@@ -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>

View File

@@ -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;
}
`;

View 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} />;
};

View File

@@ -1,3 +1,4 @@
export * from './CurrencyTag';
export * from './CurrencyTag';
export * from './FlagIcon'

View File

@@ -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 &&
`

View File

@@ -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);

View 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}
/>
);
}

View 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} />;
}

View File

@@ -0,0 +1,2 @@
export * from './WarehouseSelect';
export * from './WarehouseMultiSelect';

View File

@@ -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,
};