Skip to content

Commit

Permalink
SOV-3008: Proposal voting status (progress) (#565)
Browse files Browse the repository at this point in the history
* feat: add bar component with threshold option

* SOV-3008: bitocracy voting result component
  • Loading branch information
creed-victor committed Sep 20, 2023
1 parent 9525b34 commit a1a9572
Show file tree
Hide file tree
Showing 8 changed files with 282 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/silver-mayflies-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sovryn/ui': patch
---

SOV-3008: add bar component with threshold option
141 changes: 141 additions & 0 deletions apps/frontend/src/app/3_organisms/ProposalVotingResults/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import React, { FC, useMemo } from 'react';

import { t } from 'i18next';

import {
Bar,
Button,
ButtonStyle,
HelperButton,
Paragraph,
ParagraphSize,
} from '@sovryn/ui';
import { Decimal } from '@sovryn/utils';

import { useBlockNumber } from '../../../hooks/useBlockNumber';
import { translations } from '../../../locales/i18n';
import { Proposal } from '../../../utils/graphql/rsk/generated';
import { decimalic, formatValue } from '../../../utils/math';

export type ProposalVotingResultsProps = {
proposal: Proposal;
onExportCSV?: () => void;
};

export const ProposalVotingResults: FC<ProposalVotingResultsProps> = ({
proposal,
onExportCSV,
}) => {
const currentBlock = useBlockNumber().value;
const { votesFor, votesAgainst, endBlock, emittedBy, quorum } = proposal;
const { majorityPercentageVotes, quorumPercentageVotes } = emittedBy;

const { support, turnout, votes } = useMemo(() => {
const votes = Decimal.fromBigNumberString(votesFor).add(
Decimal.fromBigNumberString(votesAgainst),
);

const support = Decimal.fromBigNumberString(votesFor)
.div(votes)
.mul(100)
.toNumber();

const turnout = votes
.div(
Decimal.fromBigNumberString(quorum).div(
decimalic(quorumPercentageVotes).div(100),
),
)
.mul(100)
.toNumber();

return {
votes,
support,
turnout,
};
}, [quorum, quorumPercentageVotes, votesAgainst, votesFor]);

return (
<div className="w-full bg-gray-90 p-6">
<div className="flex flex-row justify-between mb-8">
<Paragraph size={ParagraphSize.base} className="font-medium">
{t(translations.proposalVotingResults.title)}
</Paragraph>
{currentBlock < endBlock ? (
<Paragraph size={ParagraphSize.small} className="italic">
{t(translations.proposalVotingResults.inProgress)}
</Paragraph>
) : (
<Button
text={t(translations.proposalVotingResults.exportCSV)}
onClick={onExportCSV}
style={ButtonStyle.secondary}
/>
)}
</div>

<div>
<div className="flex items-center gap-1 font-semibold text-xs">
{t(translations.proposalVotingResults.support.title)}{' '}
<HelperButton
tooltipClassName="min-w-48"
content={
<>
<Paragraph className="font-bold">
{t(translations.proposalVotingResults.support.required)}
</Paragraph>
<Paragraph>
{t(translations.proposalVotingResults.support.requiredValue, {
value: formatValue(majorityPercentageVotes, 3),
})}
</Paragraph>
<Paragraph className="font-bold mt-2">
{t(translations.proposalVotingResults.support.current)}
</Paragraph>
<Paragraph>
{t(translations.proposalVotingResults.support.currentValue, {
value: formatValue(support, 3),
})}
</Paragraph>
</>
}
/>
</div>
<Bar value={support} threshold={majorityPercentageVotes} />
</div>

<div className="mt-4">
<div className="flex items-center gap-1 font-semibold text-xs">
{t(translations.proposalVotingResults.quorum.title)}{' '}
<HelperButton
tooltipClassName="min-w-48"
content={
<>
<Paragraph className="font-bold">
{t(translations.proposalVotingResults.quorum.required)}
</Paragraph>
<Paragraph>
{t(translations.proposalVotingResults.quorum.requiredValue, {
value: formatValue(Decimal.fromBigNumberString(quorum), 4),
percent: formatValue(quorumPercentageVotes, 3),
})}
</Paragraph>
<Paragraph className="font-bold mt-2">
{t(translations.proposalVotingResults.quorum.current)}
</Paragraph>
<Paragraph>
{t(translations.proposalVotingResults.quorum.currentValue, {
value: formatValue(votes, 4),
percent: formatValue(turnout, 3),
})}
</Paragraph>
</>
}
/>
</div>
<Bar value={turnout} threshold={quorumPercentageVotes} />
</div>
</div>
);
};
23 changes: 23 additions & 0 deletions apps/frontend/src/app/5_pages/BitocracyPage/BitocracyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,31 @@ import {
} from '@sovryn/ui';

import { useAccount } from '../../../hooks/useAccount';
import { ProposalVotingResults } from '../../3_organisms/ProposalVotingResults';
import { translations } from '../../../locales/i18n';
import { useGetPersonalStakingStatistics } from '../StakePage/components/PersonalStakingStatistics/hooks/useGetPersonalStakingStatistics';
import { Proposals } from './components/Proposals/Proposals';
import { useGetProposals } from './hooks/useGetProposals';
import { Proposal } from '../../../utils/graphql/rsk/generated';

const pageTranslations = translations.bitocracyPage;

// DUMMY DATA, REMOVE WHEN REAL DATA IS AVAILABLE
const proposal = {
id: '0x6496df39d000478a7a7352c01e0e713835051ccd-32',
votesFor: '25642394317449679336562867',
votesAgainst: '9204395962492958076500991',
endBlock: 5505959,
startBlock: 5505952,
quorum: '20928484835262004265672060',
majorityPercentage: '73249696923417014929852210',
emittedBy: {
id: '0x6496df39d000478a7a7352c01e0e713835051ccd',
majorityPercentageVotes: 70,
quorumPercentageVotes: 20,
},
} as Proposal;

const BitocracyPage: FC = () => {
const { account } = useAccount();
const { votingPower } = useGetPersonalStakingStatistics();
Expand Down Expand Up @@ -63,6 +81,11 @@ const BitocracyPage: FC = () => {
</div>
)}
<Proposals proposals={proposals} loading={loading} />

<div className="mt-12" />
<ProposalVotingResults proposal={proposal} />
<div className="mt-12" />
<ProposalVotingResults proposal={{ ...proposal, endBlock: 0 }} />
</div>
</>
);
Expand Down
19 changes: 19 additions & 0 deletions apps/frontend/src/locales/en/translations.json
Original file line number Diff line number Diff line change
Expand Up @@ -1165,5 +1165,24 @@
"lendingTx": {
"deposit": "Deposit {{symbol}}",
"withdraw": "Withdraw {{symbol}}"
},
"proposalVotingResults": {
"title": "Voting results",
"inProgress": "Voting in progress",
"exportCSV": "Export CSV",
"support": {
"title": "Support",
"required": "Support required:",
"current": "Current support:",
"requiredValue": "> {{value}}%",
"currentValue": "{{value}}%"
},
"quorum": {
"title": "Turnout",
"required": "Turnout required:",
"current": "Current turnout:",
"requiredValue": "{{value}} VP ({{percent}}%)",
"currentValue": "{{value}} VP ({{percent}}%)"
}
}
}
20 changes: 20 additions & 0 deletions packages/ui/src/1_atoms/Bar/Bar.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.container {
@apply w-full relative h-8 flex flex-col justify-center items-center;
}

.background {
@apply w-full bg-gray-50 h-[0.356rem] relative rounded;
}

.foreground {
@apply h-[0.356rem] bg-primary-30 rounded;
}

.threshold {
@apply absolute h-[1.875rem] w-[0.52rem] flex flex-col justify-between items-center;
top: -0.76rem;
}

.threshold-line {
@apply w-[0.125rem] h-[0.475rem] bg-gray-40;
}
34 changes: 34 additions & 0 deletions packages/ui/src/1_atoms/Bar/Bar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Story, Meta } from '@storybook/react';

import React, { ComponentProps } from 'react';

import { Bar } from './Bar';

export default {
title: 'Atoms/Bar',
component: Bar,
} as Meta;

const Template: Story<ComponentProps<typeof Bar>> = args => <Bar {...args} />;

export const Default = Template.bind({});
Default.args = {
value: 50,
};

Default.argTypes = {
value: {
control: 'number',
description: 'Value of the bar',
},
threshold: {
control: 'number',
description: 'Threshold of the bar',
},
};

export const WithThreshold = Template.bind({});
WithThreshold.args = {
value: 50,
threshold: 80,
};
39 changes: 39 additions & 0 deletions packages/ui/src/1_atoms/Bar/Bar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { FC, useMemo } from 'react';

import styles from './Bar.module.css';

export type BarProps = {
value: number;
threshold?: number;
};

export const Bar: FC<BarProps> = ({ value, threshold }) => {
const normalized = useMemo(
() => ({
value: Math.min(100, Math.max(0, value ?? 0)),
threshold: Math.min(100, Math.max(0, threshold ?? 0)),
hasThreshold: threshold !== undefined,
}),
[value, threshold],
);

return (
<div className={styles.container}>
<div className={styles.background}>
<div
className={styles.foreground}
style={{ width: `${normalized.value}%` }}
></div>
{normalized.hasThreshold && (
<div
className={styles.threshold}
style={{ left: `calc(${normalized.threshold}% - 0.26rem)` }}
>
<div className={styles['threshold-line']} />
<div className={styles['threshold-line']} />
</div>
)}
</div>
</div>
);
};
1 change: 1 addition & 0 deletions packages/ui/src/1_atoms/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export * from './DynamicValue';
export * from './Lottie';
export * from './ErrorBadge';
export * from './Toggle';
export * from './Bar/Bar';

0 comments on commit a1a9572

Please sign in to comment.