feat: architect transactions locking.

This commit is contained in:
a.bouhuolia
2021-12-15 11:59:39 +02:00
parent 7cd2b1c533
commit d7da0ad24e
5 changed files with 472 additions and 373 deletions

View File

@@ -0,0 +1,72 @@
import React from 'react';
import * as R from 'ramda';
import {
TransactionsLockingList,
TransactionsLockingFull,
TransactionLockingSkeletonList,
} from './components';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import { useTransactionsLockingContext } from './TransactionsLockingProvider';
/**
* Transactions locking body.
* @returns {JSX}
*/
function TransactionsLockingBodyJsx({
// #withDialogActions
openDialog,
// #withAlertsActions
openAlert,
}) {
const {
isTransactionLockingLoading,
transactionLockingType,
} = useTransactionsLockingContext();
// Handle locking transactions.
const handleLockingTransactions = (module) => {
openDialog('locking-transactions', { module: module });
};
// Handle unlocking transactions
const handleUnlockTransactions = (module) => {
openDialog('unlocking-transactions', { module: module });
};
// Handle unlocking transactions
const handleUnlockingPartial = (module) => {
openDialog('unlocking-partial-transactions', { module: module });
};
// Handle cancel.
const handleCancelUnlockingPartail = (module) => {
openAlert('cancel-unlocking-partail', { module: module });
};
return !isTransactionLockingLoading ? (
transactionLockingType === 'partial' ? (
<TransactionsLockingList
onLock={handleLockingTransactions}
onCancelLock={handleUnlockTransactions}
onUnlockPartial={handleUnlockingPartial}
onCancelUnlockPartial={handleCancelUnlockingPartail}
/>
) : (
<TransactionsLockingFull
onLock={handleLockingTransactions}
onCancelLock={handleUnlockTransactions}
onUnlockPartial={handleUnlockingPartial}
onCancelUnlockPartial={handleCancelUnlockingPartail}
/>
)
) : (
<TransactionLockingSkeletonList />
);
}
export const TransactionsLockingBody = R.compose(
withAlertsActions,
withDialogActions,
)(TransactionsLockingBodyJsx);

View File

@@ -0,0 +1,103 @@
import React from 'react';
import { Intent } from '@blueprintjs/core';
import styled from 'styled-components';
import { useTransactionsLockingContext } from './TransactionsLockingProvider';
import {
ButtonLink,
AppToaster,
Join,
FormattedMessage as T,
Alert,
AlertDesc,
} from 'components';
import {
validateMoveToFullLocking,
validateMoveToPartialLocking,
} from './utils';
/**
* Transactions locking header.
* @returns
*/
export function TransactionsLockingHeader() {
const {
transactionsLocking,
transactionLockingType,
setTransactionLockingType,
} = useTransactionsLockingContext();
// Handle all lock link click.
const handleAllLockClick = () => {
const activeModules = validateMoveToFullLocking(
transactionsLocking.modules,
);
const modulesStrong = activeModules.map((module) => (
<strong>{module.formatted_module}</strong>
));
if (activeModules.length > 0) {
AppToaster.show({
message: (
<span>
You should unlock <Join items={modulesStrong} sep={', '} /> modules
first, than you can lock all transactions at once.
</span>
),
intent: Intent.DANGER,
});
} else {
setTransactionLockingType('all');
}
};
const handleUndividualLockClick = () => {
const isAllLockingActive = validateMoveToPartialLocking(
transactionsLocking.all,
);
if (isAllLockingActive) {
AppToaster.show({
message:
'You should unlock all transactions at once before, than lock transactions partially on each module.',
intent: Intent.DANGER,
});
} else {
setTransactionLockingType('partial');
}
};
return transactionLockingType !== 'all' ? (
<LockAllAlert
title={<T id={'transactions_locking_lock_all_transactions_at_once'} />}
intent={Intent.PRIMARY}
>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<ButtonLink onClick={handleAllLockClick}>
<T id={'transactions_locking.lock_all_transactions_at_once'} />
</ButtonLink>
</LockAllAlert>
) : (
<LockAllAlert title={'Lock Individual Modules'} intent={Intent.PRIMARY}>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<ButtonLink onClick={handleUndividualLockClick}>
<T id={'transactions_locking.lock_modules_individually'} />
</ButtonLink>
</LockAllAlert>
);
}
const LockAllAlert = styled(Alert)`
margin-bottom: 0;
margin-top: 20px;
background: transparent;
${AlertDesc} {
color: #1f3255;
}
`;

View File

@@ -1,223 +1,10 @@
import React from 'react';
import { Intent } from '@blueprintjs/core';
import styled from 'styled-components';
import * as R from 'ramda';
import {
Alert,
ButtonLink,
AppToaster,
Join,
Paragraph,
FormattedMessage as T,
AlertDesc,
} from 'components';
import { Paragraph } from 'components';
import { TransactionsLockingProvider } from './TransactionsLockingProvider';
import {
TransactionLockingContent,
TransactionLockingItemLoading,
} from './components';
import { useTransactionsLockingContext } from './TransactionsLockingProvider';
import withDialogActions from 'containers/Dialog/withDialogActions';
import withAlertsActions from 'containers/Alert/withAlertActions';
import {
validateMoveToFullLocking,
validateMoveToPartialLocking,
} from './utils';
function TransactionsLockingList({
items,
onLock,
onUnlock,
onUnlockPartial,
onCancel,
}) {
return items.map(
({
is_enabled,
is_partial_unlock,
module,
formatted_module,
description,
...item
}) => (
<TransactionLockingContent
name={formatted_module}
module={module}
description={description}
isEnabled={is_enabled}
isPartialUnlock={is_partial_unlock}
onLock={onLock}
onUnlockFull={onUnlock}
onUnlockPartial={onUnlockPartial}
onEditLock={onLock}
onCancle={onCancel}
lockToDate={item.formatted_lock_to_date}
lockReason={item.lock_reason}
unlockReason={item.unlock_reason}
unlockFromDate={item.formatted_unlock_from_date}
unlockToDate={item.formatted_unlock_to_date}
/>
),
);
}
function TransactionsLockingFull({ onLock, onUnlock, onUnlockPartial }) {
const {
transactionsLocking: { all },
} = useTransactionsLockingContext();
return (
<TransactionLockingContent
name={all.formatted_module}
description={all.description}
isEnabled={all.is_enabled}
onLock={onLock}
onUnlockPartial={onUnlockPartial}
onEditLock={onUnlock}
/>
);
}
function TransactionLockingSkeletonList() {
return (
<>
<TransactionLockingItemLoading />
<TransactionLockingItemLoading />
<TransactionLockingItemLoading />
</>
);
}
function TransactionsLockingAlert() {
const {
transactionsLocking,
transactionLockingType,
setTransactionLockingType,
} = useTransactionsLockingContext();
// Handle all lock link click.
const handleAllLockClick = () => {
const activeModules = validateMoveToFullLocking(
transactionsLocking.modules,
);
const modulesStrong = activeModules.map((module) => (
<strong>{module.formatted_module}</strong>
));
if (activeModules.length > 0) {
AppToaster.show({
message: (
<span>
You should unlock <Join items={modulesStrong} sep={', '} /> modules
first, than you can lock all transactions at once.
</span>
),
intent: Intent.DANGER,
});
} else {
setTransactionLockingType('all');
}
};
const handleUndividualLockClick = () => {
const isAllLockingActive = validateMoveToPartialLocking(
transactionsLocking.all,
);
if (isAllLockingActive) {
AppToaster.show({
message:
'You should unlock all transactions at once before, than lock transactions partially on each module.',
intent: Intent.DANGER,
});
} else {
setTransactionLockingType('partial');
}
};
return transactionLockingType !== 'all' ? (
<LockAllAlert
title={<T id={'transactions_locking_lock_all_transactions_at_once'} />}
intent={Intent.PRIMARY}
>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<ButtonLink onClick={handleAllLockClick}>
<T id={'transactions_locking.lock_all_transactions_at_once'} />
</ButtonLink>
</LockAllAlert>
) : (
<LockAllAlert title={'Lock Individual Modules'} intent={Intent.PRIMARY}>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</p>
<ButtonLink onClick={handleUndividualLockClick}>
<T id={'transactions_locking.lock_modules_individually'} />
</ButtonLink>
</LockAllAlert>
);
}
function TransactionsLockingBodyJsx({
// #withDialogActions
openDialog,
// #withAlertsActions
openAlert,
}) {
const {
transactionsLocking: { modules },
isTransactionLockingLoading,
transactionLockingType,
} = useTransactionsLockingContext();
// Handle locking transactions.
const handleLockingTransactions = (module) => {
openDialog('locking-transactions', { module: module });
};
// Handle unlocking transactions
const handleUnlockTransactions = (module) => {
openDialog('unlocking-transactions', { module: module });
};
// Handle unlocking transactions
const handleUnlockingPartial = (module) => {
openDialog('unlocking-partial-transactions', { module: module });
};
// Handle cancel.
const handleCancelUnlockingPartail = (module) => {
openAlert('cancel-unlocking-partail', { module: module });
};
return !isTransactionLockingLoading ? (
transactionLockingType === 'partial' ? (
<TransactionsLockingList
items={modules}
onLock={handleLockingTransactions}
onUnlock={handleUnlockTransactions}
onUnlockPartial={handleUnlockingPartial}
onCancel={handleCancelUnlockingPartail}
/>
) : (
<TransactionsLockingFull
onLock={handleLockingTransactions}
onUnlockPartial={handleUnlockingPartial}
/>
)
) : (
<TransactionLockingSkeletonList />
);
}
const TransactionsLockingBody = R.compose(
withAlertsActions,
withDialogActions,
)(TransactionsLockingBodyJsx);
import { TransactionsLockingHeader } from './TransactionsLockingHeader';
import { TransactionsLockingBody } from './TransactionsLockingBody';
/**
* Transactions locking list.
@@ -233,7 +20,7 @@ export default function TransactionsLockingListPage() {
ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua.
</TransLockingDesc>
<TransactionsLockingAlert />
<TransactionsLockingHeader />
</TransactionsLockingParagraph>
<TransactionsLockingBody />
@@ -241,6 +28,7 @@ export default function TransactionsLockingListPage() {
</TransactionsLockingProvider>
);
}
const TransactionsLocking = styled.div`
display: flex;
flex-direction: column;
@@ -255,13 +43,3 @@ const TransactionsLockingParagraph = styled(Paragraph)`
`;
const TransLockingDesc = styled.p``;
const LockAllAlert = styled(Alert)`
margin-bottom: 0;
margin-top: 20px;
background: transparent;
${AlertDesc} {
color: #1f3255;
}
`;

View File

@@ -9,13 +9,80 @@ import {
Intent,
Divider,
Classes,
Tag,
} from '@blueprintjs/core';
import { Hint, Icon, If, FormattedMessage as T } from 'components';
import { Popover2 } from '@blueprintjs/popover2';
import { Hint, Icon, If, FormattedMessage as T } from 'components';
import { useTransactionsLockingContext } from './TransactionsLockingProvider';
import { safeInvoke } from 'utils';
export const TransactionLockingItemLoading = ({}) => {
/**
* Transaction locking module item.
* @returns {React.JSX}
*/
export function TransactionsLockingItemModule({ module, ...rest }) {
return (
<TransactionLockingContent
name={module.formatted_module}
module={module.module}
description={module.description}
isEnabled={module.is_enabled}
isPartialUnlock={module.is_partial_unlock}
lockToDate={module.formatted_lock_to_date}
lockReason={module.lock_reason}
unlockReason={module.unlock_reason}
unlockFromDate={module.formatted_unlock_from_date}
unlockToDate={module.formatted_unlock_to_date}
{...rest}
/>
);
}
/**
* Transactions locking items modules list.
* @returns {React.JSX}
*/
export function TransactionsLockingList({ ...rest }) {
const {
transactionsLocking: { modules },
} = useTransactionsLockingContext();
return modules.map((module) => (
<TransactionsLockingItemModule module={module} {...rest} />
));
}
/**
* Transactions locking full module item.
* @returns {React.JSX}
*/
export function TransactionsLockingFull({ ...rest }) {
const {
transactionsLocking: { all },
} = useTransactionsLockingContext();
return <TransactionsLockingItemModule module={all} {...rest} />;
}
/**
* Transactions locking skeleton list.
* @returns {React.JSX}
*/
export function TransactionLockingSkeletonList() {
return (
<>
<TransactionLockingItemSkeleton />
<TransactionLockingItemSkeleton />
<TransactionLockingItemSkeleton />
</>
);
}
/**
* Transactions locking skeleton item.
* @returns {React.JSX}
*/
export const TransactionLockingItemSkeleton = ({}) => {
return (
<TransactionLockingWrapp>
<TransLockingInner>
@@ -37,27 +104,152 @@ export const TransactionLockingItemLoading = ({}) => {
);
};
export const TransactionLockingContent = ({
name,
description,
module,
const TransactionsLockingItemContext = React.createContext();
isEnabled,
lockToDate,
lockReason,
const useTransactionsLockingItemContext = () =>
React.useContext(TransactionsLockingItemContext);
// Unlock props.
isPartialUnlock,
unlockToDate,
unlockFromDate,
unlockReason,
/**
* Transactions locking item.
* @returns {React.JSX}
*/
export const TransactionLockingContent = (props) => {
const {
name,
description,
module,
isEnabled,
lockToDate,
lockReason,
// Unlock props.
isPartialUnlock,
unlockToDate,
unlockFromDate,
unlockReason,
onLock,
onCancelLock,
onEditLock,
onUnlockPartial,
onCancelUnlockPartial,
} = props;
return (
<TransactionsLockingItemContext.Provider value={props}>
<TransactionLockingWrapp isEnabled={isEnabled}>
<TransLockingInner>
<TransLockingIcon>
<Icon icon="lock" iconSize={24} />
</TransLockingIcon>
<TransactionsLockingItemContent />
<TransactionsLockingItemActions />
</TransLockingInner>
</TransactionLockingWrapp>
</TransactionsLockingItemContext.Provider>
);
};
/**
* Transactions locking item content.
*/
function TransactionsLockingItemContent() {
const {
name,
description,
isEnabled,
lockToDate,
lockReason,
// Unlock props.
isPartialUnlock,
unlockToDate,
unlockFromDate,
unlockReason,
} = useTransactionsLockingItemContext();
return (
<TransLockingContent>
<TransLockingItemTitle>
{name}
<Hint content={description} position={Position.BOTTOM_LEFT} />
</TransLockingItemTitle>
<If condition={!isEnabled}>
<TransLockingItemDesc>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</TransLockingItemDesc>
</If>
<If condition={isEnabled}>
<TransLockWrap>
<TransLockingItemDesc>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.of_the_module_locked_to' },
{
value: lockToDate,
},
)}
</TransLockingItemDesc>
<If condition={lockReason}>
<TransLockingReason>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.lock_reason' },
{ value: lockReason },
)}
</TransLockingReason>
</If>
</TransLockWrap>
</If>
<If condition={isPartialUnlock}>
<TransUnlockWrap>
<TransLockingItemDesc>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.partial_unlocked_from' },
{
fromDate: unlockFromDate,
toDate: unlockToDate,
},
)}
</TransLockingItemDesc>
<If condition={unlockReason}>
<TransLockingReason>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.unlock_reason' },
{ value: unlockReason },
)}
</TransLockingReason>
</If>
</TransUnlockWrap>
</If>
</TransLockingContent>
);
}
/**
* Transactions locking item actions.
*/
function TransactionsLockingItemActions() {
const {
module,
isEnabled,
// Unlock props.
isPartialUnlock,
onLock,
onCancelLock,
onEditLock,
onUnlockPartial,
onCancelUnlockPartial,
} = useTransactionsLockingItemContext();
onLock,
onEditLock,
onUnlockFull,
onUnlockPartial,
onCancle,
}) => {
const handleLockClick = (event) => {
safeInvoke(onLock, module, event);
};
@@ -69,136 +261,68 @@ export const TransactionLockingContent = ({
};
const handleUnlockFull = (event) => {
safeInvoke(onUnlockFull, module, event);
safeInvoke(onCancelLock, module, event);
};
const handleCanclel = (event) => {
safeInvoke(onCancle, module, event);
const handleCancelPartialUnlock = (event) => {
safeInvoke(onCancelUnlockPartial, module, event);
};
return (
<TransactionLockingWrapp isEnabled={isEnabled}>
<TransLockingInner>
<TransLockingIcon>
<Icon icon="lock" iconSize={24} />
</TransLockingIcon>
<TransLockingActions>
<If condition={!isEnabled}>
<Button
small={true}
minimal={true}
intent={Intent.PRIMARY}
onClick={handleLockClick}
>
<T id={'transactions_locking.lock'} />
</Button>
</If>
<TransLockingContent>
<TransLockingItemTitle>
{name}
<Hint content={description} position={Position.BOTTOM_LEFT} />
</TransLockingItemTitle>
<If condition={isEnabled}>
<Button
small={true}
minimal={true}
intent={Intent.PRIMARY}
onClick={handleEditBtn}
>
<T id={'edit'} />
</Button>
<Divider />
<Popover2
content={
<Menu>
<MenuItem
text={<T id={'transactions_locking.full_unlock'} />}
onClick={handleUnlockFull}
/>
<If condition={!isEnabled}>
<TransLockingItemDesc>
Lorem ipsum dolor sit amet, consectetur adipisicing elit.
</TransLockingItemDesc>
</If>
<If condition={isEnabled}>
<TransLockWrap>
<TransLockingItemDesc>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.of_the_module_locked_to' },
{
value: lockToDate,
},
)}
</TransLockingItemDesc>
<If condition={lockReason}>
<TransLockingReason>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.lock_reason' },
{ value: lockReason },
)}
</TransLockingReason>
<If condition={!isPartialUnlock}>
<MenuItem
text={<T id={'transactions_locking.paetial_unlock'} />}
onClick={handleUnlockPartial}
/>
</If>
</TransLockWrap>
</If>
<If condition={isPartialUnlock}>
<TransUnlockWrap>
<TransLockingItemDesc>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.partial_unlocked_from' },
{
fromDate: unlockFromDate,
toDate: unlockToDate,
},
)}
</TransLockingItemDesc>
<If condition={unlockReason}>
<TransLockingReason>
{intl.formatHTMLMessage(
{ id: 'transactions_locking.unlock_reason' },
{ value: unlockReason },
)}
</TransLockingReason>
<If condition={isPartialUnlock}>
<MenuItem
text={<T id={'transactions_locking.cancel_partial_unlock'} />}
onClick={handleCancelPartialUnlock}
/>
</If>
</TransUnlockWrap>
</If>
</TransLockingContent>
<TransLockingActions>
<If condition={!isEnabled}>
<Button
small={true}
minimal={true}
intent={Intent.PRIMARY}
onClick={handleLockClick}
>
<T id={'transactions_locking.lock'} />
</Button>
</If>
<If condition={isEnabled}>
<Button
small={true}
minimal={true}
intent={Intent.PRIMARY}
onClick={handleEditBtn}
>
<T id={'edit'} />
</Button>
<Divider />
<Popover2
content={
<Menu>
<MenuItem
text={<T id={'transactions_locking.full_unlock'} />}
onClick={handleUnlockFull}
/>
<If condition={!isPartialUnlock}>
<MenuItem
text={<T id={'transactions_locking.paetial_unlock'} />}
onClick={handleUnlockPartial}
/>
</If>
<If condition={isPartialUnlock}>
<MenuItem
text={
<T id={'transactions_locking.cancel_partial_unlock'} />
}
onClick={handleCanclel}
/>
</If>
</Menu>
}
placement={'bottom-start'}
minimal={true}
>
<Button small={true} minimal={true} intent={Intent.PRIMARY}>
<T id={'transactions_locking.unlock'} />
</Button>
</Popover2>
</If>
</TransLockingActions>
</TransLockingInner>
</TransactionLockingWrapp>
</Menu>
}
placement={'bottom-start'}
minimal={true}
>
<Button small={true} minimal={true} intent={Intent.PRIMARY}>
<T id={'transactions_locking.unlock'} />
</Button>
</Popover2>
</If>
</TransLockingActions>
);
};
}
const TransactionLockingWrapp = styled.div`
display: flex;
@@ -288,6 +412,7 @@ const TransUnlockWrap = styled.div`
font-size: 13px;
}
`;
const TransLockWrap = styled.div`
${TransLockingReason} {
margin-top: 10px;

View File

@@ -1,9 +1,30 @@
export const validateMoveToPartialLocking = (all) => {
return all.is_enabled;
}
};
export const validateMoveToFullLocking = (modules) => {
return modules.filter((module) => module.is_enabled);
}
};
export const transformItem = (item) => {
return {
name: item.formatted_module,
module: item.module,
description: item.description,
isEnabled: item.is_enabled,
isPartialUnlock: item.is_partial_unlock,
lockToDate: item.formatted_lock_to_date,
lockReason: item.lock_reason,
unlockFromDate: item.formatted_unlock_from_date,
unlockToDate: item.formatted_unlock_to_date,
unlockReason: item.unlock_reason,
partialUnlockReason: item.partial_unlock_reason,
};
};
export const transformList = (res) => {
return {
all: transformItem(res.all),
modules: res.modules.map((module) => transformItem(module)),
};
};