Merge pull request #99 from poanetwork/update-new-ballot

Update new ballot
This commit is contained in:
Victor Baranov 2018-03-20 22:29:41 +03:00 committed by GitHub
commit 244f6ee173
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 1808 additions and 1291 deletions

1585
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,6 +39,7 @@
"mobx-react-router": "^4.0.1", "mobx-react-router": "^4.0.1",
"moment": "^2.20.1", "moment": "^2.20.1",
"node-sass-chokidar": "0.0.3", "node-sass-chokidar": "0.0.3",
"nodemon": "^1.14.12",
"npm-run-all": "^4.1.2", "npm-run-all": "^4.1.2",
"object-assign": "4.1.1", "object-assign": "4.1.1",
"postcss-flexbugs-fixes": "3.2.0", "postcss-flexbugs-fixes": "3.2.0",
@ -65,8 +66,8 @@
"scripts": { "scripts": {
"predeploy": "npm run build", "predeploy": "npm run build",
"deploy": "gh-pages -d build -o origin", "deploy": "gh-pages -d build -o origin",
"build-css": "node-sass-chokidar src/assets/App.scss -o src/assets", "build-css": "node-sass-chokidar src/assets/App.scss -o src/assets --output-style=compressed",
"watch-css": "npm run build-css && node-sass-chokidar src/assets/App.scss -o src/assets/src/assets --watch", "watch-css": "nodemon -e scss -x \"npm run build-css\"",
"start-js": "node scripts/start.js", "start-js": "node scripts/start.js",
"start": "npm-run-all -p watch-css start-js", "start": "npm-run-all -p watch-css start-js",
"build-js": "node scripts/build.js", "build-js": "node scripts/build.js",

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,7 @@
@import 'stylesheets/settings'; @import 'stylesheets/settings';
@import 'stylesheets/socials'; @import 'stylesheets/socials';
@import 'stylesheets/vote-scale'; @import 'stylesheets/vote-scale';
@import 'stylesheets/ballot-types';
@import 'stylesheets/ballots/placeholders'; @import 'stylesheets/ballots/placeholders';
@import 'stylesheets/ballots/base'; @import 'stylesheets/ballots/base';
@import 'stylesheets/ballots/about'; @import 'stylesheets/ballots/about';
@ -23,4 +24,4 @@
@import 'stylesheets/select/menu'; @import 'stylesheets/select/menu';
@import 'stylesheets/select/mixins'; @import 'stylesheets/select/mixins';
@import 'stylesheets/select/multi'; @import 'stylesheets/select/multi';
@import 'stylesheets/select/spinner'; @import 'stylesheets/select/spinner';

View File

@ -0,0 +1,30 @@
.ballot-types {
border-bottom: 1px solid #e5eef9;
padding: 0 30px 10px;
&-i {
cursor: pointer;
position: relative;
margin-bottom: 20px;
&:before {
content: '';
position: absolute;
left: -30px;
top: 50%;
opacity: 0;
height: 30px;
width: 3px;
transform: translateY(-50%);
background-color: #08b3f2;
}
&_active {
color: #08b3f2;
&:before {
opacity: 1;
}
}
}
}

View File

@ -6,7 +6,8 @@ label {
font-family: 'Open Sans', sans-serif; font-family: 'Open Sans', sans-serif;
} }
input { input,
textarea {
@extend %form-el; @extend %form-el;
&:focus { &:focus {
@ -18,6 +19,14 @@ input {
} }
} }
textarea {
width: 100%;
padding-top: 10px;
padding-bottom: 10px;
height: 70px;
resize: none;
}
select { select {
@include image-2x('./images/select@2x.png', 8px, 4px); @include image-2x('./images/select@2x.png', 8px, 4px);
@extend %form-el; @extend %form-el;

View File

@ -1,15 +1,54 @@
.info { .info {
@include image-2x('./images/info@2x.png', 32px, 32px);
display: flex;
align-items: center;
padding-left: 44px;
min-height: 32px;
background-image: url(./images/info.png);
background-repeat: no-repeat;
background-position: left center;
color: #8197a2; color: #8197a2;
.ballots & { .ballots & {
margin-bottom: 20px; @include image-2x('./images/info@2x.png', 32px, 32px);
display: flex;
align-items: center;
padding-left: 44px;
min-height: 32px;
background-image: url(./images/info.png);
background-repeat: no-repeat;
background-position: left center;
}
.new-form & {
padding: 30px;
}
&-title {
position: relative;
margin-bottom: 30px;
padding-left: 44px;
&:before {
@include image-2x('./images/info@2x.png', 32px, 32px);
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 32px;
height: 32px;
}
}
&-i {
position: relative;
margin-bottom: 10px;
padding-left: 12px;
line-height: 18px;
font-size: 12px;
&:before {
content: '';
position: absolute;
left: 0;
top: 7px;
width: 4px;
height: 4px;
border-radius: 50%;
background-color: #08b3f2;
}
} }
} }

View File

@ -1,15 +1,41 @@
.new { .new {
&-form { &-form {
@extend %white-block; @extend %white-block;
display: flex;
overflow: hidden; overflow: hidden;
padding: 0;
&-footer { @media screen and (max-width: $tablet-width) {
display: flex; display: block;
align-items: center; }
justify-content: space-between;
@media screen and (max-width: $mobile-width) { &-side {
flex-direction: column; padding-top: 30px;
padding-bottom: 30px;
box-sizing: border-box;
@media screen and (max-width: $tablet-width) {
width: 100% !important;
}
&_left {
width: 30%;
@media screen and (max-width: $tablet-width) {
padding-bottom: 0;
}
}
&_right {
width: 70%;
border-left: 1px solid #e5eef9;
padding-left: 30px;
padding-right: 30px;
@media screen and (max-width: $tablet-width) {
border: 0;
padding-top: 0;
}
} }
} }
} }

View File

@ -48,6 +48,11 @@ export class NewBallot extends React.Component {
if (ballotStore.isBallotForKey) { if (ballotStore.isBallotForKey) {
for (let ballotKeysProp in ballotStore.ballotKeys) { for (let ballotKeysProp in ballotStore.ballotKeys) {
if (!ballotStore.ballotKeys[ballotKeysProp]) {
swal("Warning!", `Ballot ${ballotKeysProp} is empty`, "warning");
commonStore.hideLoading();
return false;
}
if (ballotStore.ballotKeys[ballotKeysProp].length === 0) { if (ballotStore.ballotKeys[ballotKeysProp].length === 0) {
swal("Warning!", `Ballot ${ballotKeysProp} is empty`, "warning"); swal("Warning!", `Ballot ${ballotKeysProp} is empty`, "warning");
commonStore.hideLoading(); commonStore.hideLoading();
@ -99,6 +104,12 @@ export class NewBallot extends React.Component {
} }
} }
if (!ballotStore.isBallotForKey && !ballotStore.isBallotForMinThreshold && !ballotStore.isBallotForProxy) {
swal("Warning!", messages.BALLOT_TYPE_IS_EMPTY_MSG, "warning");
commonStore.hideLoading();
return false;
}
return true; return true;
} }
@ -107,8 +118,8 @@ export class NewBallot extends React.Component {
const inputToMethod = { const inputToMethod = {
startTime: curDateInSeconds, startTime: curDateInSeconds,
endTime: ballotStore.endTimeUnix, endTime: ballotStore.endTimeUnix,
affectedKey: ballotStore.ballotKeys.affectedKey, affectedKey: ballotStore.ballotKeys.affectedKey,
affectedKeyType: ballotStore.ballotKeys.keyType, affectedKeyType: ballotStore.ballotKeys.keyType,
miningKey: ballotStore.ballotKeys.miningKey.value, miningKey: ballotStore.ballotKeys.miningKey.value,
ballotType: ballotStore.ballotKeys.keysBallotType, ballotType: ballotStore.ballotKeys.keysBallotType,
sender: contractsStore.votingKey, sender: contractsStore.votingKey,
@ -123,7 +134,7 @@ export class NewBallot extends React.Component {
const inputToMethod = { const inputToMethod = {
startTime: curDateInSeconds, startTime: curDateInSeconds,
endTime: ballotStore.endTimeUnix, endTime: ballotStore.endTimeUnix,
proposedValue: ballotStore.ballotMinThreshold.proposedValue, proposedValue: ballotStore.ballotMinThreshold.proposedValue,
sender: contractsStore.votingKey, sender: contractsStore.votingKey,
memo: ballotStore.memo memo: ballotStore.memo
}; };
@ -136,7 +147,7 @@ export class NewBallot extends React.Component {
const inputToMethod = { const inputToMethod = {
startTime: curDateInSeconds, startTime: curDateInSeconds,
endTime: ballotStore.endTimeUnix, endTime: ballotStore.endTimeUnix,
proposedValue: ballotStore.ballotProxy.proposedAddress, proposedValue: ballotStore.ballotProxy.proposedAddress,
contractType: ballotStore.ballotProxy.contractType, contractType: ballotStore.ballotProxy.contractType,
sender: contractsStore.votingKey, sender: contractsStore.votingKey,
memo: ballotStore.memo memo: ballotStore.memo
@ -159,8 +170,8 @@ export class NewBallot extends React.Component {
if (isFormValid) { if (isFormValid) {
if (ballotStore.ballotType === ballotStore.BallotType.keys) { if (ballotStore.ballotType === ballotStore.BallotType.keys) {
const inputToAreBallotParamsValid = { const inputToAreBallotParamsValid = {
affectedKey: ballotStore.ballotKeys.affectedKey, affectedKey: ballotStore.ballotKeys.affectedKey,
affectedKeyType: ballotStore.ballotKeys.keyType, affectedKeyType: ballotStore.ballotKeys.keyType,
miningKey: ballotStore.ballotKeys.miningKey.value, miningKey: ballotStore.ballotKeys.miningKey.value,
ballotType: ballotStore.ballotKeys.keysBallotType ballotType: ballotStore.ballotKeys.keysBallotType
}; };
@ -173,13 +184,13 @@ export class NewBallot extends React.Component {
let methodToCreateBallot; let methodToCreateBallot;
switch (ballotStore.ballotType) { switch (ballotStore.ballotType) {
case ballotStore.BallotType.keys: case ballotStore.BallotType.keys:
methodToCreateBallot = this.createBallotForKeys; methodToCreateBallot = this.createBallotForKeys;
break; break;
case ballotStore.BallotType.minThreshold: case ballotStore.BallotType.minThreshold:
methodToCreateBallot = this.createBallotForMinThreshold; methodToCreateBallot = this.createBallotForMinThreshold;
break; break;
case ballotStore.BallotType.proxy: case ballotStore.BallotType.proxy:
methodToCreateBallot = this.createBallotForProxy; methodToCreateBallot = this.createBallotForProxy;
break; break;
default: default:
@ -205,6 +216,15 @@ export class NewBallot extends React.Component {
} }
} }
menuItemActive = (ballotType) => {
const { ballotStore } = this.props;
if (ballotType == ballotStore.ballotType) {
return 'ballot-types-i ballot-types-i_active';
} else {
return 'ballot-types-i';
}
}
render() { render() {
const { contractsStore, ballotStore } = this.props; const { contractsStore, ballotStore } = this.props;
let validator = ballotStore.isNewValidatorPersonalData ? <Validator />: ""; let validator = ballotStore.isNewValidatorPersonalData ? <Validator />: "";
@ -212,15 +232,15 @@ export class NewBallot extends React.Component {
let metadata let metadata
let minThreshold = 0; let minThreshold = 0;
switch (ballotStore.ballotType) { switch (ballotStore.ballotType) {
case ballotStore.BallotType.keys: case ballotStore.BallotType.keys:
metadata = <BallotKeysMetadata />; metadata = <BallotKeysMetadata />;
minThreshold = contractsStore.keysBallotThreshold; minThreshold = contractsStore.keysBallotThreshold;
break; break;
case ballotStore.BallotType.minThreshold: case ballotStore.BallotType.minThreshold:
metadata = <BallotMinThresholdMetadata />; metadata = <BallotMinThresholdMetadata />;
minThreshold = contractsStore.minThresholdBallotThreshold; minThreshold = contractsStore.minThresholdBallotThreshold;
break; break;
case ballotStore.BallotType.proxy: case ballotStore.BallotType.proxy:
metadata = <BallotProxyMetadata />; metadata = <BallotProxyMetadata />;
minThreshold = contractsStore.proxyBallotThreshold; minThreshold = contractsStore.proxyBallotThreshold;
break; break;
@ -231,67 +251,99 @@ export class NewBallot extends React.Component {
<section className="container new"> <section className="container new">
<h1 className="title">New Ballot</h1> <h1 className="title">New Ballot</h1>
<form action="" className="new-form"> <form action="" className="new-form">
<div className="hidden"> <div className="new-form-side new-form-side_left">
<div className="left"> <div className="ballot-types">
<div className="radio-container"> <div
<input type="radio" name="ballot-type" id="ballot-for-validators" className={this.menuItemActive(ballotStore.BallotType.keys)}
value={ballotStore.BallotType.keys} onClick={(e) => ballotStore.changeBallotType(e, ballotStore.BallotType.keys)}
checked={ballotStore.isBallotForKey} >
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.keys)} Validator Management Ballot
/> </div>
<label htmlFor="ballot-for-validators" className="radio">Validator Management Ballot</label> <div
<p className="hint"> className={this.menuItemActive(ballotStore.BallotType.minThreshold)}
Ballot to add, remove or swap any type of key for existing or new validators. onClick={(e) => ballotStore.changeBallotType(e, ballotStore.BallotType.minThreshold)}
</p> >
Consenus Thershold Ballot
</div>
<div
className={this.menuItemActive(ballotStore.BallotType.proxy)}
onClick={(e) => ballotStore.changeBallotType(e, ballotStore.BallotType.proxy)}
>
Modify Proxy Contract Ballot
</div> </div>
</div> </div>
<div className="right">
<div className="radio-container">
<input type="radio" name="ballot-type" id="ballot-for-consensus"
value={ballotStore.BallotType.minThreshold}
checked={ballotStore.isBallotForMinThreshold}
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.minThreshold)}
/>
<label htmlFor="ballot-for-consensus" className="radio">Consenus Threshold Ballot</label>
<p className="hint">
Ballot to change the minimum threshold for consensus to vote for keys.
</p>
</div>
</div>
<div className="left">
<div className="radio-container">
<input type="radio" name="ballot-type" id="ballot-for-proxy"
value={ballotStore.BallotType.proxy}
checked={ballotStore.isBallotForProxy}
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.proxy)}
/>
<label htmlFor="ballot-for-proxy" className="radio">Modify Proxy Contract Ballot</label>
<p className="hint">
Ballot to change one of the proxy contracts.
</p>
</div>
</div>
</div>
<hr />
{validator}
{keysTypes}
{metadata}
<div className="form-el">
<label>Description of the ballot</label>
<div>
<textarea rows="4"
value={ballotStore.memo}
onChange={(e) => ballotStore.setMemo(e)}
></textarea>
</div>
</div>
<div className="new-form-footer">
<div className="info"> <div className="info">
Minimum {minThreshold} from {contractsStore.validatorsLength} validators required to pass the proposal<br /> <p className="info-title">Information of the ballot</p>
You can create {contractsStore.validatorLimits.keys} ballot for keys<br /> <div className="info-i">
You can create {contractsStore.validatorLimits.minThreshold} ballot for consensus<br /> Minimum {minThreshold} from {contractsStore.validatorsLength} validators required to pass the proposal<br />
You can create {contractsStore.validatorLimits.proxy} ballot for proxy<br /> </div>
<div className="info-i">
You can create {contractsStore.validatorLimits.keys} ballot for keys<br />
</div>
<div className="info-i">
You can create {contractsStore.validatorLimits.minThreshold} ballot for consensus<br />
</div>
<div className="info-i">
You can create {contractsStore.validatorLimits.proxy} ballot for proxy<br />
</div>
</div> </div>
</div>
<div className="new-form-side new-form-side_right">
<div className="form-el">
<label>Description of the ballot</label>
<div>
<textarea rows="4"
value={ballotStore.memo}
onChange={(e) => ballotStore.setMemo(e)}
></textarea>
</div>
</div>
<hr />
<div className="hidden">
<div className="left">
<div className="radio-container">
<input type="radio" name="ballot-type" id="ballot-for-validators"
value={ballotStore.BallotType.keys}
checked={ballotStore.isBallotForKey}
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.keys)}
/>
<label htmlFor="ballot-for-validators" className="radio">Validator Management Ballot</label>
<p className="hint">
Ballot to add, remove or swap any type of key for existing or new validators.
</p>
</div>
</div>
<div className="right">
<div className="radio-container">
<input type="radio" name="ballot-type" id="ballot-for-consensus"
value={ballotStore.BallotType.minThreshold}
checked={ballotStore.isBallotForMinThreshold}
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.minThreshold)}
/>
<label htmlFor="ballot-for-consensus" className="radio">Consenus Threshold Ballot</label>
<p className="hint">
Ballot to change the minimum threshold for consensus to vote for keys.
</p>
</div>
</div>
<div className="left">
<div className="radio-container">
<input type="radio" name="ballot-type" id="ballot-for-proxy"
value={ballotStore.BallotType.proxy}
checked={ballotStore.isBallotForProxy}
onChange={e => ballotStore.changeBallotType(e, ballotStore.BallotType.proxy)}
/>
<label htmlFor="ballot-for-proxy" className="radio">Modify Proxy Contract Ballot</label>
<p className="hint">
Ballot to change one of the proxy contracts.
</p>
</div>
</div>
</div>
<hr />
{validator}
{keysTypes}
{metadata}
<button type="button" className="add-ballot" onClick={e => this.onClick(e)}>Add ballot</button> <button type="button" className="add-ballot" onClick={e => this.onClick(e)}>Add ballot</button>
</div> </div>
</form> </form>

View File

@ -12,6 +12,7 @@ messages.AFFECTED_KEY_IS_NOT_ADDRESS_MSG = "Ballot affectedKey isn't address";
messages.MINING_KEY_IS_NOT_ADDRESS_MSG = "Ballot miningKey isn't address"; messages.MINING_KEY_IS_NOT_ADDRESS_MSG = "Ballot miningKey isn't address";
messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG = "Ballot proposedAddress isn't address"; messages.PROPOSED_ADDRESS_IS_NOT_ADDRESS_MSG = "Ballot proposedAddress isn't address";
messages.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG = "Ballot end time should be greater than now"; messages.END_TIME_SHOULD_BE_GREATER_THAN_NOW_MSG = "Ballot end time should be greater than now";
messages.BALLOT_TYPE_IS_EMPTY_MSG = "Ballot type is empty";
messages.NO_METAMASK_MSG = `You haven't chosen any account in MetaMask. messages.NO_METAMASK_MSG = `You haven't chosen any account in MetaMask.
Please, choose your voting key in MetaMask and reload the page. Please, choose your voting key in MetaMask and reload the page.
Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`; Check POA Network <a href='https://github.com/poanetwork/wiki' target='blank'>wiki</a> for more info.`;