Skip to content

Commit 7937cac

Browse files
authored
Feat: add VaultItemUi component (#548)
* Feat: add VaultItemUi component * review fixes * review fixes 2 * console.log->noop
1 parent 200b1cc commit 7937cac

File tree

4 files changed

+318
-0
lines changed

4 files changed

+318
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import React from 'react';
2+
import BigNumber from 'bignumber.js';
3+
import noop from 'noop-ts';
4+
import { ComponentMeta } from '@storybook/react';
5+
import { withCenterStory, withThemeProvider } from 'stories/decorators';
6+
import { VaultItemUi } from '.';
7+
8+
export default {
9+
title: 'Components/VaultItemUi',
10+
component: VaultItemUi,
11+
decorators: [withCenterStory({ width: 800 }), withThemeProvider],
12+
parameters: {
13+
backgrounds: {
14+
default: 'Default',
15+
},
16+
},
17+
} as ComponentMeta<typeof VaultItemUi>;
18+
19+
export const VaultItemUiDefault = () => (
20+
<VaultItemUi
21+
tokenId="vai"
22+
rewardTokenId="xvs"
23+
rewardWei={new BigNumber('000900000000000000')}
24+
userStakedWei={new BigNumber('100000000000000000000')}
25+
stakingAprPercentage={2.39}
26+
dailyEmissionWei={new BigNumber('2120000000000000000')}
27+
totalStakedWei={new BigNumber('1233000000000000000000')}
28+
onClaim={noop}
29+
onStake={noop}
30+
onReward={noop}
31+
/>
32+
);
33+
34+
export const VaultItemUiWithoutReward = () => (
35+
<VaultItemUi
36+
tokenId="vrt"
37+
rewardTokenId="vrt"
38+
rewardWei={new BigNumber(0)}
39+
userStakedWei={new BigNumber('100000000000000000000')}
40+
stakingAprPercentage={2.39}
41+
dailyEmissionWei={new BigNumber('2120000000000000000')}
42+
totalStakedWei={new BigNumber('1233000000000000000000')}
43+
onClaim={noop}
44+
onStake={noop}
45+
onReward={noop}
46+
/>
47+
);
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// VaultItemUi
2+
/** @jsxImportSource @emotion/react */
3+
import React, { useMemo } from 'react';
4+
import BigNumber from 'bignumber.js';
5+
import Paper from '@mui/material/Paper';
6+
import Typography from '@mui/material/Typography';
7+
8+
import { useTranslation } from 'translation';
9+
import { convertWeiToCoins, formatToReadablePercentage } from 'utilities/common';
10+
import { TokenId } from 'types';
11+
import { getToken } from 'utilities';
12+
import { Icon } from '../Icon';
13+
import { Button } from '../Button';
14+
import { useStyles } from './styles';
15+
16+
export interface IVaultItemUiProps {
17+
tokenId: TokenId;
18+
rewardTokenId: TokenId;
19+
rewardWei?: BigNumber;
20+
userStakedWei: BigNumber;
21+
stakingAprPercentage: number;
22+
dailyEmissionWei: BigNumber;
23+
totalStakedWei: BigNumber;
24+
onClaim: () => void;
25+
onStake: () => void;
26+
onReward: () => void;
27+
className?: string;
28+
}
29+
30+
export const VaultItemUi = ({
31+
tokenId,
32+
rewardTokenId,
33+
rewardWei,
34+
userStakedWei,
35+
stakingAprPercentage,
36+
dailyEmissionWei,
37+
totalStakedWei,
38+
onClaim,
39+
onStake,
40+
onReward,
41+
className,
42+
}: IVaultItemUiProps) => {
43+
const styles = useStyles();
44+
const { t } = useTranslation();
45+
46+
const dataListItems = useMemo(
47+
() => [
48+
{
49+
title: t('vaultItemUi.stakingApr', { stakeTokenName: getToken(tokenId).symbol }),
50+
value: formatToReadablePercentage(stakingAprPercentage),
51+
},
52+
{
53+
title: t('vaultItemUi.dailyEmission'),
54+
value: (
55+
<>
56+
<Icon css={styles.tokenIcon} name={rewardTokenId} />
57+
{convertWeiToCoins({
58+
valueWei: dailyEmissionWei,
59+
tokenId: rewardTokenId,
60+
returnInReadableFormat: true,
61+
})}
62+
</>
63+
),
64+
},
65+
{
66+
title: t('vaultItemUi.totalStaked'),
67+
value: (
68+
<>
69+
<Icon css={styles.tokenIcon} name={tokenId} />
70+
{convertWeiToCoins({
71+
valueWei: totalStakedWei,
72+
tokenId,
73+
returnInReadableFormat: true,
74+
})}
75+
</>
76+
),
77+
},
78+
],
79+
[tokenId, rewardTokenId, stakingAprPercentage, dailyEmissionWei, totalStakedWei],
80+
);
81+
82+
return (
83+
<Paper css={styles.container} className={className}>
84+
<div css={styles.header}>
85+
<div css={styles.title}>
86+
<Icon css={styles.tokenIcon} name={tokenId} />
87+
<Typography variant="h4" css={styles.text}>
88+
{getToken(tokenId).symbol}
89+
</Typography>
90+
</div>
91+
92+
{rewardWei?.isGreaterThan(0) && (
93+
<div css={styles.rewardWrapper}>
94+
<Typography css={[styles.text, styles.textMobile14]}>
95+
{t('vaultItemUi.reward')}
96+
</Typography>
97+
<Icon css={[styles.tokenIcon, styles.tokenIconReward]} name={rewardTokenId} />
98+
<Typography
99+
css={[styles.text, styles.textRewardValue, styles.textMobile14]}
100+
variant="body1"
101+
color="textPrimary"
102+
>
103+
{convertWeiToCoins({
104+
valueWei: rewardWei,
105+
tokenId: rewardTokenId,
106+
returnInReadableFormat: true,
107+
})}
108+
</Typography>
109+
<Button onClick={onClaim} variant="text" css={styles.buttonClaim}>
110+
{t('vaultItemUi.claimButton')}
111+
</Button>
112+
</div>
113+
)}
114+
</div>
115+
116+
<Typography css={styles.textMobile14}>{t('vaultItemUi.youAreStaking')}</Typography>
117+
<Typography variant="h1" css={styles.textStakingValue}>
118+
<Icon css={[styles.tokenIcon, styles.tokenIconLarge]} name={tokenId} />
119+
{convertWeiToCoins({
120+
tokenId,
121+
valueWei: userStakedWei,
122+
returnInReadableFormat: true,
123+
})}
124+
</Typography>
125+
126+
<ul css={styles.dataRow}>
127+
{dataListItems.map(({ title, value }) => (
128+
<li key={title} css={styles.valueWrapper}>
129+
<Typography css={styles.textMobile14}>{title}</Typography>
130+
<Typography variant="h4" css={[styles.textAligned, styles.textMobile14]}>
131+
{value}
132+
</Typography>
133+
</li>
134+
))}
135+
</ul>
136+
137+
<div css={styles.buttonsWrapper}>
138+
<Button onClick={onStake} css={styles.button} variant="primary">
139+
{t('vaultItemUi.stakeButton')}
140+
</Button>
141+
<Button onClick={onReward} css={styles.button} variant="secondary">
142+
{t('vaultItemUi.withdrawButton')}
143+
</Button>
144+
</div>
145+
</Paper>
146+
);
147+
};
148+
149+
export default VaultItemUi;
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { css } from '@emotion/react';
2+
import { useTheme } from '@mui/material';
3+
4+
export const useStyles = () => {
5+
const theme = useTheme();
6+
7+
return {
8+
container: css`
9+
width: 100%;
10+
`,
11+
header: css`
12+
display: flex;
13+
align-items: center;
14+
justify-content: space-between;
15+
margin-bottom: ${theme.spacing(6)};
16+
`,
17+
title: css`
18+
display: flex;
19+
align-items: center;
20+
`,
21+
tokenIcon: css`
22+
width: ${theme.shape.iconSize.large}px;
23+
height: ${theme.shape.iconSize.large}px;
24+
margin-right: ${theme.spacing(1)};
25+
`,
26+
tokenIconLarge: css`
27+
width: ${theme.shape.iconSize.xLarge}px;
28+
height: ${theme.shape.iconSize.xLarge}px;
29+
`,
30+
tokenIconReward: css`
31+
margin-left: ${theme.spacing(1)};
32+
`,
33+
text: css`
34+
display: inline;
35+
`,
36+
textMobile14: css`
37+
${theme.breakpoints.down('sm')} {
38+
font-size: ${theme.spacing(3.5)};
39+
}
40+
`,
41+
textRewardValue: css`
42+
font-weight: 600;
43+
`,
44+
textStakingValue: css`
45+
display: inline-flex;
46+
align-items: center;
47+
`,
48+
textAligned: css`
49+
display: inline-flex;
50+
align-items: center;
51+
`,
52+
rewardWrapper: css`
53+
display: flex;
54+
align-items: center;
55+
56+
${theme.breakpoints.down('sm')} {
57+
font-size: ${theme.spacing(3.5)};
58+
}
59+
`,
60+
buttonClaim: css`
61+
padding: 0;
62+
margin-left: ${theme.spacing(3)};
63+
`,
64+
dataRow: css`
65+
display: flex;
66+
padding-left: 0;
67+
margin-top: ${theme.spacing(6)};
68+
${theme.breakpoints.down('sm')} {
69+
flex-direction: column;
70+
}
71+
`,
72+
valueWrapper: css`
73+
display: block;
74+
75+
${theme.breakpoints.down('sm')} {
76+
display: flex;
77+
justify-content: space-between;
78+
}
79+
80+
& + & {
81+
border-left: 1px solid ${theme.palette.interactive.delimiter};
82+
margin-left: ${theme.spacing(8)};
83+
padding-left: ${theme.spacing(8)};
84+
85+
${theme.breakpoints.down('sm')} {
86+
margin-left: 0;
87+
padding-left: 0;
88+
border: none;
89+
margin-top: ${theme.spacing(2)};
90+
}
91+
}
92+
`,
93+
buttonsWrapper: css`
94+
display: flex;
95+
justify-content: space-between;
96+
margin-top: ${theme.spacing(8)};
97+
98+
${theme.breakpoints.down('sm')} {
99+
flex-direction: column;
100+
margin-top: ${theme.spacing(4)};
101+
}
102+
`,
103+
button: css`
104+
width: calc(50% - ${theme.spacing(2)});
105+
106+
${theme.breakpoints.down('sm')} {
107+
width: 100%;
108+
margin-top: ${theme.spacing(3)};
109+
}
110+
`,
111+
};
112+
};

src/translation/translations/en.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,5 +529,15 @@
529529
"dailyDistribution": "Daily Distribution:",
530530
"progressBar": "Xvs Distribution progress",
531531
"remaining": "Remaining:"
532+
},
533+
"vaultItemUi": {
534+
"reward": "Reward:",
535+
"claimButton": "Claim",
536+
"youAreStaking": "You are staking",
537+
"stakingApr": "{{stakeTokenName}} Staking APR",
538+
"dailyEmission": "Daily Emission",
539+
"totalStaked": "Total Staked",
540+
"stakeButton": "Stake",
541+
"withdrawButton": "Withdraw"
532542
}
533543
}

0 commit comments

Comments
 (0)