Skip to content

Commit

Permalink
Create Escrow UI (#290)
Browse files Browse the repository at this point in the history
* codify create escrow modal. add in escrow column. add create escrow action button. add tooltips to assets table action buttons

* fix label

* fix table alignments
  • Loading branch information
hornilla-jowelyn authored Dec 1, 2023
1 parent ec0b8b6 commit 0a12988
Show file tree
Hide file tree
Showing 7 changed files with 354 additions and 26 deletions.
6 changes: 6 additions & 0 deletions src/components/Assets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,11 @@ const Assets = () => {
account.modals.revokeDelegate.setVisibility(true);
};

const handleCreateEscrowClick = (asset?: AssetHoldingsTableItem) => {
account.assets.selectAssetHolding(asset);
account.modals.createEscrow.setVisibility(true);
};

// const handleLinkClick = (assetHolding?: AssetHoldingsTableItem) => {
// router.push(`/dao/${assetHolding?.asset?.dao_id}`);
// };
Expand Down Expand Up @@ -256,6 +261,7 @@ const Assets = () => {
onDelegateClick={handleDelegateClick}
onRedelegateClick={handleRedelegateClick}
onOpenLinkClick={handleLinkClick}
onCreateEscrowClick={handleCreateEscrowClick}
/>
) : (
<div>Sorry, no assets found</div>
Expand Down
81 changes: 55 additions & 26 deletions src/components/AssetsTable.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { stringShorten } from '@polkadot/util';
import { Tooltip } from 'antd';
import cn from 'classnames';
import Image from 'next/image';

import type { Asset, AssetHolding } from '@/services/assets';
import type { RawDao } from '@/services/daos';
import agreement from '@/svg/agreement.svg';
import coinsTransfer from '@/svg/coinsTransfer.svg';
import delegate from '@/svg/delegate.svg';
import openLink from '@/svg/openlink.svg';
Expand All @@ -23,6 +25,7 @@ interface AssestHoldingsTableProps {
onOpenLinkClick?: (assetHolding?: AssetHoldingsTableItem) => void;
onDelegateClick?: (assetHolding?: AssetHoldingsTableItem) => void;
onRedelegateClick?: (assetHolding?: AssetHoldingsTableItem) => void;
onCreateEscrowClick?: (assetHolding?: AssetHoldingsTableItem) => void;
}

const AssetItemRow = ({
Expand All @@ -31,6 +34,7 @@ const AssetItemRow = ({
onOpenLinkClick,
onDelegateClick,
onRedelegateClick,
onCreateEscrowClick,
}: Omit<AssestHoldingsTableProps, 'assetHoldings'> & {
assetHolding: AssetHoldingsTableItem;
}) => {
Expand All @@ -46,7 +50,7 @@ const AssetItemRow = ({

return (
<div
className='grid grid-cols-[auto_10%_15%_15%_15%_15%] gap-2 space-x-2 rounded-lg border-[0.3px] border-solid
className='grid grid-cols-[1.5fr_1fr_1.5fr_1fr_1fr_1.5fr_2fr] gap-2 space-x-2 rounded-lg border-[0.3px] border-solid
border-neutral-focus px-4 py-3 text-sm font-normal text-neutral-focus'>
<span className='flex items-center gap-2'>
<div className='relative flex items-center justify-center'>
Expand Down Expand Up @@ -76,6 +80,7 @@ const AssetItemRow = ({
? stringShorten(assetHolding.delegateAddress)
: '-'}
</span>
<span className='my-auto'>-</span>
<span className='my-auto'>
{uiTokens(
assetHolding.balance,
Expand All @@ -85,37 +90,58 @@ const AssetItemRow = ({
</span>
<span className='my-auto flex gap-2'>
<span
className='rounded-full border border-solid border-neutral-focus p-2 hover:border-primary'
className='rounded-full border border-solid border-neutral-focus p-[6px] hover:border-primary'
onClick={() => onTransferClick && onTransferClick(assetHolding)}>
<Image
src={coinsTransfer}
alt='transfer'
width={16}
height={16}
className='m-auto cursor-pointer'
/>
<Tooltip title='Transfer Tokens'>
<Image
src={coinsTransfer}
alt='transfer'
width={20}
height={20}
className='m-auto cursor-pointer'
/>
</Tooltip>
</span>
<span
className='rounded-full border border-solid border-neutral-focus p-2 hover:border-primary'
className='rounded-full border border-solid border-neutral-focus p-[6px] hover:border-primary'
onClick={handleDelegateRedelegate}>
<Image
src={delegate}
alt='transfer'
width={16}
height={16}
className='m-auto cursor-pointer'
/>
<Tooltip title='Delegate Voting Power'>
<Image
src={delegate}
alt='transfer'
width={20}
height={20}
className='m-auto cursor-pointer'
/>
</Tooltip>
</span>
<span
className='rounded-full border border-solid border-neutral-focus p-2 hover:border-primary'
className='rounded-full border border-solid border-neutral-focus p-[6px] hover:border-primary'
onClick={() => onOpenLinkClick && onOpenLinkClick(assetHolding)}>
<Image
src={openLink}
alt='open link'
width={16}
height={16}
className='m-auto cursor-pointer'
/>
<Tooltip title='Open PolkadotJS'>
<Image
src={openLink}
alt='open link'
width={20}
height={20}
className='m-auto cursor-pointer'
/>
</Tooltip>
</span>
<span
className='rounded-full border border-solid border-neutral-focus p-[6px] hover:border-primary'
onClick={() =>
onCreateEscrowClick && onCreateEscrowClick(assetHolding)
}>
<Tooltip title='Create Escrow'>
<Image
src={agreement}
alt='open link'
width={20}
height={20}
className='m-auto cursor-pointer'
/>
</Tooltip>
</span>
</span>
</div>
Expand All @@ -128,14 +154,16 @@ const AssetsHoldingsTable = ({
onOpenLinkClick,
onDelegateClick,
onRedelegateClick,
onCreateEscrowClick,
}: AssestHoldingsTableProps) => {
return (
<div className='w-full'>
<div className='grid grid-cols-[auto_10%_15%_15%_15%_15%] gap-2 space-x-2 px-4 py-3 text-sm font-normal text-neutral-focus'>
<div className='grid grid-cols-[1.5fr_1fr_1.5fr_1fr_1fr_1.5fr_2fr] gap-2 space-x-2 px-4 py-3 text-sm font-normal text-neutral-focus'>
<span>DAO NAME</span>
<span>DAO ID</span>
<span>Role</span>
<span>Delegate</span>
<span>In Escrow</span>
<span>Owned Tokens</span>
<span>Actions</span>
</div>
Expand All @@ -148,6 +176,7 @@ const AssetsHoldingsTable = ({
onDelegateClick={onDelegateClick}
onRedelegateClick={onRedelegateClick}
onOpenLinkClick={onOpenLinkClick}
onCreateEscrowClick={onCreateEscrowClick}
/>
))}
</div>
Expand Down
220 changes: 220 additions & 0 deletions src/components/CreateEscrowModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import { ErrorMessage } from '@hookform/error-message';
import Modal from 'antd/lib/modal';
import cn from 'classnames';
import { useEffect, useState } from 'react';
import type { SubmitHandler } from 'react-hook-form';
import { useForm } from 'react-hook-form';

// import useGenesisDao from '@/hooks/useGenesisDao';
import { type AssetHolding } from '@/services/assets';
import type { CreateEscrowFormValues } from '@/stores/genesisStore';
import useGenesisStore from '@/stores/genesisStore';
import { isValidPolkadotAddress } from '@/utils';

import AssetHoldingCard from './AssetHoldingCard';

const CreateEscrowModal = (props: {
open?: boolean;
assetHolding: AssetHolding;
delegateAccount?: string;
daoId?: string;
daoImage?: string | null;
isDelegated?: boolean;
onClose?: () => void;
onSuccess?: () => void;
}) => {
// const {} = useGenesisDao();

const { assetHolding, daoImage, daoId, open, onClose, onSuccess } = props;
const [isApproved, setIsApproved] = useState(false);

const [
currentWalletAccount,
modalData,
handleErrors,
apiConnection,
createApiConnection,
daoTokenTreasuryBalance,
currentDao,
] = useGenesisStore((s) => [
s.currentWalletAccount,
s.pages.account.modals.createEscrow,
s.handleErrors,
s.apiConnection,
s.createApiConnection,
s.daoTokenTreasuryBalance,
s.currentDao,
]);

const {
handleSubmit,
register,
formState: { errors },
reset,
} = useForm<CreateEscrowFormValues>();

const buttonText = () => {
if (!isApproved) {
return 'Approve';
}

if (!currentWalletAccount) {
return 'Please Connect Wallet';
}
if (modalData.txnProcessing) {
return 'Processing';
}
return 'Send';
};

const onDelegateAssetSuccess = () => {
modalData.setTxnProcessing(false);
modalData.setVisibility(false);
if (onSuccess) {
onSuccess();
}
reset();
};

const onSubmit: SubmitHandler<CreateEscrowFormValues> = () => {
if (!isApproved) {
setIsApproved(true);
} else if (onClose) {
onClose();
}
};

useEffect(() => {
if (!apiConnection) {
createApiConnection();
}
// eslint-disable-next-line
}, []);

const loading = false;

return (
<Modal
open={open}
wrapClassName='a-modal-bg'
className='a-modal'
onCancel={onClose}
footer={null}
width={615}
zIndex={99}>
<form onSubmit={handleSubmit(onSubmit)}>
<div className='px-12'>
<h2 className='mb-4 text-center text-3xl font-semibold text-primary'>
Create Escrow Wallet
</h2>
<div className='w-full space-y-8'>
<div className='flex w-full items-center'>
<p className='w-1/4'>Asset</p>
<div className='grow'>
<AssetHoldingCard
assetHolding={assetHolding}
daoImage={daoImage}
daoId={daoId}
/>
</div>
</div>
<div className='flex w-full items-center'>
<p className='w-1/4'>Account</p>
<div className='grow'>
<input
type='text'
className='input input-bordered input-primary'
placeholder='Address'
{...register('account', {
required: 'Required',
validate: (addr) =>
(addr && isValidPolkadotAddress(addr) === true) ||
'Not a valid address',
})}
disabled={loading || isApproved}
/>
<ErrorMessage
errors={errors}
name='account'
render={({ message }) => (
<p className='ml-2 mt-1 text-error'>{message}</p>
)}
/>
</div>
</div>
<div className='flex w-full items-center'>
<p className='w-1/4'>Lock Time</p>
<div className='grow'>
<input
type='number'
className='input input-bordered input-primary'
placeholder='Time in days'
{...register('vestingTime', {
required: 'Required',
validate: (days) =>
!Number.isNaN(Number(days)) || 'Not a valid value',
})}
disabled={loading || isApproved}
/>
<ErrorMessage
errors={errors}
name='vestingTime'
render={({ message }) => (
<p className='ml-2 mt-1 text-error'>{message}</p>
)}
/>
</div>
</div>
<div className='flex w-full items-center'>
<p className='w-1/4'>Amount</p>
<div className='grow'>
<input
type='number'
className='input input-bordered input-primary'
placeholder='Amount'
{...register('amount', {
// required: 'Required',
// validate: (amount) =>
// daoTokenTreasuryBalance?.gte(new BN(`${amount}`)) ||
// `Maximum value is ${uiTokens(
// daoTokenTreasuryBalance || new BN(0),
// 'dao',
// currentDao?.daoId
// )}`,
})}
disabled={loading || isApproved}
/>
<ErrorMessage
errors={errors}
name='amount'
render={({ message }) => (
<p className='ml-2 mt-1 text-error'>{message}</p>
)}
/>
</div>
</div>
<div className='mt-10 flex w-full gap-2'>
<button
className={cn('btn mr-3 w-1/2 bg-white')}
onClick={onClose}
disabled={loading}>
Cancel
</button>
<button
type='submit'
disabled={loading}
className={cn('btn btn-primary w-1/2 ', {
'btn-disabled': false,
loading,
})}>
{buttonText()}
</button>
</div>
</div>
</div>
</form>
</Modal>
);
};

export default CreateEscrowModal;
Loading

0 comments on commit 0a12988

Please sign in to comment.