Skip to content

Commit a01bf7f

Browse files
committed
CAS-647: Add Quorum Voting
1 parent 4d3029b commit a01bf7f

8 files changed

Lines changed: 132 additions & 38 deletions

File tree

frontend/packages/client/src/api/proposals.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,15 @@ export const createProposalApiReq = async ({
4848
timestamp,
4949
} = {}) => {
5050
const { communityId, ...proposalData } = proposalPayload;
51+
52+
if (proposalData.voteType === 'basic') {
53+
proposalData.choices = [
54+
{ choiceText: 'For', choiceImgUrl: null },
55+
{ choiceText: 'Against', choiceImgUrl: null },
56+
{ choiceText: 'Abstain', choiceImgUrl: null },
57+
];
58+
}
59+
5160
const url = `${COMMUNITIES_URL}/${communityId}/proposals`;
5261
const fetchOptions = {
5362
method: 'POST',

frontend/packages/client/src/components/Proposal/VoteHeader.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const Wrapper = ({ children }) => (
99
</h3>
1010
);
1111

12-
export default function VoteHeader({ status }) {
12+
export default function VoteHeader({ status, voteType = 'single-choice' }) {
1313
// Status: user-voted, invite-to-vote, is-closed
1414
const message = {
1515
'user-voted': (
@@ -20,7 +20,12 @@ export default function VoteHeader({ status }) {
2020
You successfully voted on this proposal!
2121
</div>
2222
),
23-
'invite-to-vote': <>Rank your vote &#10024;</>,
23+
'invite-to-vote': (
24+
<>
25+
{voteType === 'single-choice' ? 'Cast your vote' : 'Rank your vote'}{' '}
26+
&#10024;
27+
</>
28+
),
2429
'is-closed': <>Voting has ended on this proposal.</>,
2530
};
2631

frontend/packages/client/src/components/ProposalCreate/FormConfig.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,12 @@ const StepTwoSchema = yup.object().shape({
4646
})
4747
),
4848
})
49-
.when('voteType', (voteType, schema) =>
50-
voteType === 'single-choice'
49+
.when('voteType', (voteType, schema) => {
50+
if (voteType === 'basic') return;
51+
return voteType === 'single-choice'
5152
? schema.min(2, 'Please add a choice, minimum amount is two')
52-
: schema.min(3, 'Please add a choice, minimum amount is three')
53-
)
53+
: schema.min(3, 'Please add a choice, minimum amount is three');
54+
})
5455
.unique('value', 'Invalid duplicated option'),
5556
maxWeight: yup
5657
.string()
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
export default function BasicVoteExample() {
2+
return (
3+
<>
4+
{['For', 'Against', 'Abstain'].map((text, index) => (
5+
<div
6+
className="is-flex is-align-items-center is-justify-content-left mb-1"
7+
style={{ whiteSpace: 'nowrap', width: 75 }}
8+
key={index}
9+
>
10+
<div
11+
className="rounded-full has-background-grey has-text-white mr-2 is-flex is-align-items-center is-justify-content-center"
12+
style={{ width: 12, height: 12 }}
13+
>
14+
{index === 0 ? (
15+
<span style={{ fontSize: 7, paddingTop: 1 }}>&#x2713;</span>
16+
) : null}
17+
</div>
18+
<span className="smaller-text">{text}</span>
19+
</div>
20+
))}
21+
</>
22+
);
23+
}

frontend/packages/client/src/components/ProposalCreate/StepTwo/ChoiceOptionCreator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function ChoiceOptionCreator({
2020
setValue('tabOption', option);
2121
};
2222

23-
if (voteType === 'ranked-choice') {
23+
if (voteType === 'ranked-choice' || voteType === 'basic') {
2424
setTab('text-based');
2525
}
2626

frontend/packages/client/src/components/ProposalCreate/StepTwo/TextBasedChoices.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ const TextBasedChoices = ({
1717
remove,
1818
} = useFieldArray({
1919
control,
20-
name: 'choices',
20+
name: fieldName,
2121
focusAppend: true,
2222
});
2323

frontend/packages/client/src/components/ProposalCreate/StepTwo/VotingSelector.js

Lines changed: 82 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,45 @@
1+
import { useEffect } from 'react';
12
import { Card } from 'components/Card';
23
import { Box, Heading } from '@chakra-ui/react';
34
import { Text } from '@chakra-ui/react';
5+
import BasicVoteExample from './BasicVoteExample';
46
import ChoiceOptionCreator from './ChoiceOptionCreator';
57
import RankedVoteExample from './RankedVoteExample';
68
import SingleVoteExample from './SingleVoteExample';
79

10+
const getDescription = (voteType) => {
11+
switch (voteType) {
12+
case 'single-choice':
13+
return (
14+
<Text color={'grey.500'} mb={4}>
15+
Provide the specific options you’d like to cast votes for. Use
16+
Text-based presentation for choices that are described in words. Use
17+
Visual for side-by-side visual options represented by images.
18+
</Text>
19+
);
20+
case 'ranked-choice':
21+
return (
22+
<>
23+
<Text color={'grey.500'} mb={4}>
24+
Provide the specific options you’d like to cast votes for. Ranked
25+
Choice Voting currently only supports Text-based presentation for
26+
choices that are described in words.
27+
</Text>
28+
<Text color={'grey.500'} mb={4} fontWeight="bold" fontSize="sm">
29+
All choices will be randomized for voters
30+
</Text>
31+
</>
32+
);
33+
default:
34+
return (
35+
<Text color={'grey.500'} mb={4}>
36+
No choices required. Voters will be presented with For, Against or
37+
Abstain.
38+
</Text>
39+
);
40+
}
41+
};
42+
843
export default function VotingSelector({
944
voteType,
1045
setValue,
@@ -72,6 +107,37 @@ export default function VotingSelector({
72107
<SingleVoteExample />
73108
</div>
74109
</Card>
110+
<Card
111+
variant="votingType"
112+
mb={4}
113+
onClick={() =>
114+
setValue('voteType', 'basic', { shouldValidate: true })
115+
}
116+
className={voteType === 'basic' ? 'border-grey' : ''}
117+
>
118+
<div className="p-4">
119+
<div className="is-flex is-align-items-center mr-2">
120+
<label className="radio is-flex">
121+
<input
122+
{...register('voteType')}
123+
type="radio"
124+
value="basic"
125+
className="green-radio"
126+
/>
127+
</label>
128+
</div>
129+
</div>
130+
<div className="py-5 pr-5">
131+
<h5 className="title is-6 mb-2">Basic Voting</h5>
132+
<p>
133+
Voters vote on one option and are given three options to vote
134+
on. (For, Against or Abstain)
135+
</p>
136+
</div>
137+
<div className="has-background-light-grey p-4 is-hidden-mobile rounded-sm-br rounded-sm-tr is-flex is-flex-direction-column is-align-self-stretch is-justify-content-center">
138+
<BasicVoteExample />
139+
</div>
140+
</Card>
75141
<Card
76142
variant="votingType"
77143
mb={4}
@@ -107,35 +173,23 @@ export default function VotingSelector({
107173
</div>
108174
<div className="border-light-tablet rounded-lg columns is-flex-direction-column is-mobile m-0 p-6 p-0-mobile mb-6">
109175
<Heading as="h4" fontSize="2xl" mb={2}>
110-
Choices <span className="has-text-danger">*</span>
176+
Choices{' '}
177+
{voteType !== 'basic' ? (
178+
<span className="has-text-danger">*</span>
179+
) : null}
111180
</Heading>
112-
{voteType === 'single-choice' ? (
113-
<Text color={'grey.500'} mb={4}>
114-
Provide the specific options you’d like to cast votes for. Use
115-
Text-based presentation for choices that are described in words. Use
116-
Visual for side-by-side visual options represented by images.
117-
</Text>
118-
) : (
119-
<>
120-
<Text color={'grey.500'} mb={4}>
121-
Provide the specific options you’d like to cast votes for. Ranked
122-
Choice Voting currently only supports Text-based presentation for
123-
choices that are described in words.
124-
</Text>
125-
<Text color={'grey.500'} mb={4} fontWeight="bold" fontSize="sm">
126-
All choices will be randomized for voters
127-
</Text>
128-
</>
129-
)}
130-
<ChoiceOptionCreator
131-
setValue={setValue}
132-
error={errors['choices']}
133-
fieldName="choices"
134-
register={register}
135-
control={control}
136-
clearErrors={clearErrors}
137-
voteType={voteType}
138-
/>
181+
{getDescription(voteType)}
182+
{voteType !== 'basic' ? (
183+
<ChoiceOptionCreator
184+
setValue={setValue}
185+
error={errors['choices']}
186+
fieldName="choices"
187+
register={register}
188+
control={control}
189+
clearErrors={clearErrors}
190+
voteType={voteType}
191+
/>
192+
) : null}
139193
</div>
140194
</>
141195
);

frontend/packages/client/src/pages/Proposal.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,8 @@ export default function ProposalPage() {
412412
held in Balancer's liquidity pools.
413413
</div>
414414
)}
415-
{proposal.voteType === 'single-choice' ? (
415+
{proposal.voteType === 'single-choice' ||
416+
proposal.voteType === 'basic' ? (
416417
<SingleChoiceVote
417418
labelType="mobile"
418419
addr={user?.addr}
@@ -559,7 +560,8 @@ export default function ProposalPage() {
559560
Balancer's liquidity pools.
560561
</div>
561562
)}
562-
{proposal.voteType === 'single-choice' ? (
563+
{proposal.voteType === 'single-choice' ||
564+
proposal.voteType === 'basic' ? (
563565
<SingleChoiceVote
564566
labelType="desktop"
565567
loggedIn={user?.loggedIn}

0 commit comments

Comments
 (0)