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 React from 'react';
import { Intent } from '@blueprintjs/core';
import styled from 'styled-components'; import styled from 'styled-components';
import * as R from 'ramda';
import { import { Paragraph } from 'components';
Alert,
ButtonLink,
AppToaster,
Join,
Paragraph,
FormattedMessage as T,
AlertDesc,
} from 'components';
import { TransactionsLockingProvider } from './TransactionsLockingProvider'; import { TransactionsLockingProvider } from './TransactionsLockingProvider';
import { import { TransactionsLockingHeader } from './TransactionsLockingHeader';
TransactionLockingContent, import { TransactionsLockingBody } from './TransactionsLockingBody';
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);
/** /**
* Transactions locking list. * Transactions locking list.
@@ -233,7 +20,7 @@ export default function TransactionsLockingListPage() {
ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. tempor incididunt ut labore et dolore magna aliqua.
</TransLockingDesc> </TransLockingDesc>
<TransactionsLockingAlert /> <TransactionsLockingHeader />
</TransactionsLockingParagraph> </TransactionsLockingParagraph>
<TransactionsLockingBody /> <TransactionsLockingBody />
@@ -241,6 +28,7 @@ export default function TransactionsLockingListPage() {
</TransactionsLockingProvider> </TransactionsLockingProvider>
); );
} }
const TransactionsLocking = styled.div` const TransactionsLocking = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -255,13 +43,3 @@ const TransactionsLockingParagraph = styled(Paragraph)`
`; `;
const TransLockingDesc = styled.p``; 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, Intent,
Divider, Divider,
Classes, Classes,
Tag,
} from '@blueprintjs/core'; } from '@blueprintjs/core';
import { Hint, Icon, If, FormattedMessage as T } from 'components';
import { Popover2 } from '@blueprintjs/popover2'; import { Popover2 } from '@blueprintjs/popover2';
import { Hint, Icon, If, FormattedMessage as T } from 'components';
import { useTransactionsLockingContext } from './TransactionsLockingProvider';
import { safeInvoke } from 'utils'; 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 ( return (
<TransactionLockingWrapp> <TransactionLockingWrapp>
<TransLockingInner> <TransLockingInner>
@@ -37,7 +104,17 @@ export const TransactionLockingItemLoading = ({}) => {
); );
}; };
export const TransactionLockingContent = ({ const TransactionsLockingItemContext = React.createContext();
const useTransactionsLockingItemContext = () =>
React.useContext(TransactionsLockingItemContext);
/**
* Transactions locking item.
* @returns {React.JSX}
*/
export const TransactionLockingContent = (props) => {
const {
name, name,
description, description,
module, module,
@@ -53,35 +130,48 @@ export const TransactionLockingContent = ({
unlockReason, unlockReason,
onLock, onLock,
onCancelLock,
onEditLock, onEditLock,
onUnlockFull,
onUnlockPartial, onUnlockPartial,
onCancle, onCancelUnlockPartial,
}) => { } = props;
const handleLockClick = (event) => {
safeInvoke(onLock, module, event);
};
const handleEditBtn = (event) => {
safeInvoke(onEditLock, module, event);
};
const handleUnlockPartial = (event) => {
safeInvoke(onUnlockPartial, module, event);
};
const handleUnlockFull = (event) => {
safeInvoke(onUnlockFull, module, event);
};
const handleCanclel = (event) => {
safeInvoke(onCancle, module, event);
};
return ( return (
<TransactionsLockingItemContext.Provider value={props}>
<TransactionLockingWrapp isEnabled={isEnabled}> <TransactionLockingWrapp isEnabled={isEnabled}>
<TransLockingInner> <TransLockingInner>
<TransLockingIcon> <TransLockingIcon>
<Icon icon="lock" iconSize={24} /> <Icon icon="lock" iconSize={24} />
</TransLockingIcon> </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> <TransLockingContent>
<TransLockingItemTitle> <TransLockingItemTitle>
{name} {name}
@@ -139,7 +229,45 @@ export const TransactionLockingContent = ({
</TransUnlockWrap> </TransUnlockWrap>
</If> </If>
</TransLockingContent> </TransLockingContent>
);
}
/**
* Transactions locking item actions.
*/
function TransactionsLockingItemActions() {
const {
module,
isEnabled,
// Unlock props.
isPartialUnlock,
onLock,
onCancelLock,
onEditLock,
onUnlockPartial,
onCancelUnlockPartial,
} = useTransactionsLockingItemContext();
const handleLockClick = (event) => {
safeInvoke(onLock, module, event);
};
const handleEditBtn = (event) => {
safeInvoke(onEditLock, module, event);
};
const handleUnlockPartial = (event) => {
safeInvoke(onUnlockPartial, module, event);
};
const handleUnlockFull = (event) => {
safeInvoke(onCancelLock, module, event);
};
const handleCancelPartialUnlock = (event) => {
safeInvoke(onCancelUnlockPartial, module, event);
};
return (
<TransLockingActions> <TransLockingActions>
<If condition={!isEnabled}> <If condition={!isEnabled}>
<Button <Button
@@ -178,10 +306,8 @@ export const TransactionLockingContent = ({
</If> </If>
<If condition={isPartialUnlock}> <If condition={isPartialUnlock}>
<MenuItem <MenuItem
text={ text={<T id={'transactions_locking.cancel_partial_unlock'} />}
<T id={'transactions_locking.cancel_partial_unlock'} /> onClick={handleCancelPartialUnlock}
}
onClick={handleCanclel}
/> />
</If> </If>
</Menu> </Menu>
@@ -195,10 +321,8 @@ export const TransactionLockingContent = ({
</Popover2> </Popover2>
</If> </If>
</TransLockingActions> </TransLockingActions>
</TransLockingInner>
</TransactionLockingWrapp>
); );
}; }
const TransactionLockingWrapp = styled.div` const TransactionLockingWrapp = styled.div`
display: flex; display: flex;
@@ -288,6 +412,7 @@ const TransUnlockWrap = styled.div`
font-size: 13px; font-size: 13px;
} }
`; `;
const TransLockWrap = styled.div` const TransLockWrap = styled.div`
${TransLockingReason} { ${TransLockingReason} {
margin-top: 10px; margin-top: 10px;

View File

@@ -1,9 +1,30 @@
export const validateMoveToPartialLocking = (all) => { export const validateMoveToPartialLocking = (all) => {
return all.is_enabled; return all.is_enabled;
} };
export const validateMoveToFullLocking = (modules) => { export const validateMoveToFullLocking = (modules) => {
return modules.filter((module) => module.is_enabled); 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)),
};
};