|
|
|
@ -1,7 +1,7 @@
|
|
|
|
|
import React, { Component } from "react";
|
|
|
|
|
import { Redirect } from "react-router-dom";
|
|
|
|
|
import { withTranslation } from "react-i18next";
|
|
|
|
|
import { resolve } from "url";
|
|
|
|
|
import React, {Component} from 'react';
|
|
|
|
|
import {Redirect} from 'react-router-dom';
|
|
|
|
|
import {withTranslation} from 'react-i18next';
|
|
|
|
|
import {resolve} from 'url';
|
|
|
|
|
import {
|
|
|
|
|
Container,
|
|
|
|
|
Row,
|
|
|
|
@ -10,11 +10,20 @@ import {
|
|
|
|
|
Card,
|
|
|
|
|
CardHeader,
|
|
|
|
|
CardBody,
|
|
|
|
|
Table
|
|
|
|
|
} from "reactstrap";
|
|
|
|
|
import { i18nGrades } from "../../Util";
|
|
|
|
|
import { AppContext } from "../../AppContext";
|
|
|
|
|
import { errorMessage, Error } from "../../Errors";
|
|
|
|
|
Table,
|
|
|
|
|
} from 'reactstrap';
|
|
|
|
|
import {i18nGrades} from '../../Util';
|
|
|
|
|
import {AppContext} from '../../AppContext';
|
|
|
|
|
import {errorMessage, Error} from '../../Errors';
|
|
|
|
|
|
|
|
|
|
const meritProfileFromVotes = votes => {
|
|
|
|
|
const numGrades = Math.max(...votes) - Math.min(...votes);
|
|
|
|
|
const profile = Array(numGrades).fill(0);
|
|
|
|
|
votes.forEach(vote => {
|
|
|
|
|
profile[vote] += 1;
|
|
|
|
|
});
|
|
|
|
|
return profile;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class Result extends Component {
|
|
|
|
|
static contextType = AppContext;
|
|
|
|
@ -34,8 +43,7 @@ class Result extends Component {
|
|
|
|
|
collapseGraphics: false,
|
|
|
|
|
collapseProfiles: false,
|
|
|
|
|
electionGrades: i18nGrades(),
|
|
|
|
|
errorMessage: "",
|
|
|
|
|
numVotes:"..."
|
|
|
|
|
errorMessage: '',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -43,7 +51,7 @@ class Result extends Component {
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
response.json().then(response => {
|
|
|
|
|
this.setState(state => ({
|
|
|
|
|
errorMessage: errorMessage(response, this.props.t)
|
|
|
|
|
errorMessage: errorMessage(response, this.props.t),
|
|
|
|
|
}));
|
|
|
|
|
});
|
|
|
|
|
throw Error(response);
|
|
|
|
@ -57,24 +65,21 @@ class Result extends Component {
|
|
|
|
|
name: c.name,
|
|
|
|
|
profile: c.profile,
|
|
|
|
|
grade: c.grade,
|
|
|
|
|
score: c.score,
|
|
|
|
|
numVotes: c.num_votes
|
|
|
|
|
}));
|
|
|
|
|
console.log(response);
|
|
|
|
|
this.setState(state => ({ candidates: candidates, numVotes : candidates[0].numVotes }));
|
|
|
|
|
this.setState(state => ({candidates: candidates}));
|
|
|
|
|
return response;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
detailsToState = response => {
|
|
|
|
|
const numGrades = response.num_grades;
|
|
|
|
|
const colSizeGradeLg = Math.floor(
|
|
|
|
|
(12 - this.state.colSizeCandidateLg) / numGrades
|
|
|
|
|
(12 - this.state.colSizeCandidateLg) / numGrades,
|
|
|
|
|
);
|
|
|
|
|
const colSizeGradeMd = Math.floor(
|
|
|
|
|
(12 - this.state.colSizeCandidateMd) / numGrades
|
|
|
|
|
(12 - this.state.colSizeCandidateMd) / numGrades,
|
|
|
|
|
);
|
|
|
|
|
const colSizeGradeXs = Math.floor(
|
|
|
|
|
(12 - this.state.colSizeCandidateXs) / numGrades
|
|
|
|
|
(12 - this.state.colSizeCandidateXs) / numGrades,
|
|
|
|
|
);
|
|
|
|
|
this.setState(state => ({
|
|
|
|
|
title: response.title,
|
|
|
|
@ -94,7 +99,7 @@ class Result extends Component {
|
|
|
|
|
12 - colSizeGradeXs * numGrades > 0
|
|
|
|
|
? 12 - colSizeGradeXs * numGrades
|
|
|
|
|
: 12,
|
|
|
|
|
electionGrades: i18nGrades().slice(0, numGrades)
|
|
|
|
|
electionGrades: i18nGrades().slice(0, numGrades),
|
|
|
|
|
}));
|
|
|
|
|
return response;
|
|
|
|
|
};
|
|
|
|
@ -102,39 +107,38 @@ class Result extends Component {
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
// get details of the election
|
|
|
|
|
const electionSlug = this.props.match.params.slug;
|
|
|
|
|
if (electionSlug === "dev") {
|
|
|
|
|
if (electionSlug === 'dev') {
|
|
|
|
|
const dataTest = [
|
|
|
|
|
{
|
|
|
|
|
name: "BB",
|
|
|
|
|
name: 'BB',
|
|
|
|
|
id: 1,
|
|
|
|
|
score: 1.0,
|
|
|
|
|
profile: [1, 1, 0, 0, 0, 0, 0],
|
|
|
|
|
grade: 1
|
|
|
|
|
grade: 1,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "CC",
|
|
|
|
|
name: 'CC',
|
|
|
|
|
id: 2,
|
|
|
|
|
score: 1.0,
|
|
|
|
|
profile: [0, 0, 2, 0, 0, 0, 0],
|
|
|
|
|
grade: 2
|
|
|
|
|
grade: 2,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "AA",
|
|
|
|
|
name: 'AA',
|
|
|
|
|
id: 0,
|
|
|
|
|
score: 1.0,
|
|
|
|
|
profile: [1, 1, 0, 0, 0, 0, 0],
|
|
|
|
|
grade: 1
|
|
|
|
|
}
|
|
|
|
|
grade: 1,
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
this.setState({ candidates: dataTest });
|
|
|
|
|
console.log(this.state.candidates);
|
|
|
|
|
this.setState({candidates: dataTest});
|
|
|
|
|
} else {
|
|
|
|
|
const detailsEndpoint = resolve(
|
|
|
|
|
this.context.urlServer,
|
|
|
|
|
this.context.routesServer.getElection.replace(
|
|
|
|
|
new RegExp(":slug", "g"),
|
|
|
|
|
electionSlug
|
|
|
|
|
)
|
|
|
|
|
new RegExp(':slug', 'g'),
|
|
|
|
|
electionSlug,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
fetch(detailsEndpoint)
|
|
|
|
@ -147,9 +151,9 @@ class Result extends Component {
|
|
|
|
|
const resultsEndpoint = resolve(
|
|
|
|
|
this.context.urlServer,
|
|
|
|
|
this.context.routesServer.getResultsElection.replace(
|
|
|
|
|
new RegExp(":slug", "g"),
|
|
|
|
|
electionSlug
|
|
|
|
|
)
|
|
|
|
|
new RegExp(':slug', 'g'),
|
|
|
|
|
electionSlug,
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
fetch(resultsEndpoint)
|
|
|
|
@ -161,30 +165,30 @@ class Result extends Component {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toggleGraphics = () => {
|
|
|
|
|
this.setState(state => ({ collapseGraphics: !state.collapseGraphics }));
|
|
|
|
|
this.setState(state => ({collapseGraphics: !state.collapseGraphics}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
toggleProfiles = () => {
|
|
|
|
|
this.setState(state => ({ collapseProfiles: !state.collapseProfiles }));
|
|
|
|
|
this.setState(state => ({collapseProfiles: !state.collapseProfiles}));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
render() {
|
|
|
|
|
const { errorMessage, candidates, electionGrades } = this.state;
|
|
|
|
|
const { t } = this.props;
|
|
|
|
|
const {errorMessage, candidates, electionGrades} = this.state;
|
|
|
|
|
const {t} = this.props;
|
|
|
|
|
const grades = i18nGrades();
|
|
|
|
|
|
|
|
|
|
if (errorMessage && errorMessage !== "") {
|
|
|
|
|
|
|
|
|
|
if (errorMessage && errorMessage !== '') {
|
|
|
|
|
return <Error value={errorMessage} />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let totalOfVote = 0;
|
|
|
|
|
|
|
|
|
|
//based on the first candidate
|
|
|
|
|
if (candidates.length > 0) {
|
|
|
|
|
candidates[0].profile.map((value, i) => (totalOfVote += value));
|
|
|
|
|
} else {
|
|
|
|
|
totalOfVote = 1;
|
|
|
|
|
}
|
|
|
|
|
const sum = seq => Object.values(seq).reduce((a, b) => a + b, 0);
|
|
|
|
|
const numVotes =
|
|
|
|
|
candidates && candidates.length > 0 ? sum(candidates[0].profile) : 1;
|
|
|
|
|
const gradeIds =
|
|
|
|
|
candidates && candidates.length > 0
|
|
|
|
|
? Object.keys(candidates[0].profile)
|
|
|
|
|
: [];
|
|
|
|
|
console.log(gradeIds);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Container>
|
|
|
|
@ -196,27 +200,30 @@ class Result extends Component {
|
|
|
|
|
|
|
|
|
|
<Row className="mt-5">
|
|
|
|
|
<Col>
|
|
|
|
|
<h1>{t("Results of the election:")}</h1>
|
|
|
|
|
<h5><small>{t("Number of votes:")}{" "+this.state.numVotes}</small></h5>
|
|
|
|
|
<hr className="mb-5"/>
|
|
|
|
|
<h1>{t('Results of the election:')}</h1>
|
|
|
|
|
<h5>
|
|
|
|
|
<small>
|
|
|
|
|
{t('Number of votes:')}
|
|
|
|
|
{' ' + numVotes}
|
|
|
|
|
</small>
|
|
|
|
|
</h5>
|
|
|
|
|
<hr className="mb-5" />
|
|
|
|
|
<ol>
|
|
|
|
|
{candidates.map((candidate, i) => {
|
|
|
|
|
console.log(candidate);
|
|
|
|
|
return (
|
|
|
|
|
<li key={i} className="mt-2">
|
|
|
|
|
<span className="mt-2 ml-2">{candidate.name}</span>
|
|
|
|
|
<span className="mt-2 ml-2">{candidate.name}</span>
|
|
|
|
|
<span
|
|
|
|
|
className="badge badge-light ml-2 mt-2"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: electionGrades[candidate.grade].color,
|
|
|
|
|
color: "#fff"
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
color: '#fff',
|
|
|
|
|
}}>
|
|
|
|
|
{grades[candidate.grade].label}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="badge badge-dark mt-2 ml-2">
|
|
|
|
|
{/* <span className="badge badge-dark mt-2 ml-2">
|
|
|
|
|
{(100 * candidate.score).toFixed(1)}%
|
|
|
|
|
</span>
|
|
|
|
|
</span> */}
|
|
|
|
|
</li>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
@ -230,11 +237,10 @@ class Result extends Component {
|
|
|
|
|
<CardHeader className="pointer" onClick={this.toggleGraphics}>
|
|
|
|
|
<h4
|
|
|
|
|
className={
|
|
|
|
|
"m-0 panel-title " +
|
|
|
|
|
(this.state.collapseGraphics ? "collapsed" : "")
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{t("Graph")}
|
|
|
|
|
'm-0 panel-title ' +
|
|
|
|
|
(this.state.collapseGraphics ? 'collapsed' : '')
|
|
|
|
|
}>
|
|
|
|
|
{t('Graph')}
|
|
|
|
|
</h4>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<Collapse isOpen={this.state.collapseGraphics}>
|
|
|
|
@ -242,27 +248,26 @@ class Result extends Component {
|
|
|
|
|
<div>
|
|
|
|
|
<div
|
|
|
|
|
className="median"
|
|
|
|
|
style={{ height: candidates.length * 28 + 30 }}
|
|
|
|
|
style={{height: candidates.length * 28 + 30}}
|
|
|
|
|
/>
|
|
|
|
|
<table style={{ width: "100%" }}>
|
|
|
|
|
<table style={{width: '100%'}}>
|
|
|
|
|
<tbody>
|
|
|
|
|
{candidates.map((candidate, i) => {
|
|
|
|
|
return (
|
|
|
|
|
<tr key={i}>
|
|
|
|
|
<td style={{ width: "30px" }}>{i + 1}</td>
|
|
|
|
|
<td style={{width: '30px'}}>{i + 1}</td>
|
|
|
|
|
{/*candidate.label*/}
|
|
|
|
|
<td>
|
|
|
|
|
<table style={{ width: "100%" }}>
|
|
|
|
|
<table style={{width: '100%'}}>
|
|
|
|
|
<tbody>
|
|
|
|
|
<tr>
|
|
|
|
|
{candidate.profile.map((value, i) => {
|
|
|
|
|
{gradeIds.map((id, i) => {
|
|
|
|
|
const value = candidate.profile[id];
|
|
|
|
|
if (value > 0) {
|
|
|
|
|
let percent =
|
|
|
|
|
Math.round(
|
|
|
|
|
(value * 100) / totalOfVote
|
|
|
|
|
) + "%";
|
|
|
|
|
(value * 100) / numVotes + '%';
|
|
|
|
|
if (i === 0) {
|
|
|
|
|
percent = "auto";
|
|
|
|
|
percent = 'auto';
|
|
|
|
|
}
|
|
|
|
|
return (
|
|
|
|
|
<td
|
|
|
|
@ -270,9 +275,8 @@ class Result extends Component {
|
|
|
|
|
style={{
|
|
|
|
|
width: percent,
|
|
|
|
|
backgroundColor: this.state
|
|
|
|
|
.electionGrades[i].color
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
.electionGrades[i].color,
|
|
|
|
|
}}>
|
|
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
);
|
|
|
|
@ -295,7 +299,7 @@ class Result extends Component {
|
|
|
|
|
{candidates.map((candidate, i) => {
|
|
|
|
|
return (
|
|
|
|
|
<span key={i}>
|
|
|
|
|
{i > 0 ? ", " : ""}
|
|
|
|
|
{i > 0 ? ', ' : ''}
|
|
|
|
|
<b>{i + 1}</b>: {candidate.name}
|
|
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
@ -311,9 +315,8 @@ class Result extends Component {
|
|
|
|
|
className="badge badge-light mr-2 mt-2"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: grade.color,
|
|
|
|
|
color: "#fff"
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
color: '#fff',
|
|
|
|
|
}}>
|
|
|
|
|
{grade.label}
|
|
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
@ -331,11 +334,10 @@ class Result extends Component {
|
|
|
|
|
<CardHeader className="pointer" onClick={this.toggleProfiles}>
|
|
|
|
|
<h4
|
|
|
|
|
className={
|
|
|
|
|
"m-0 panel-title " +
|
|
|
|
|
(this.state.collapseProfiles ? "collapsed" : "")
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
{t("Preference profile")}
|
|
|
|
|
'm-0 panel-title ' +
|
|
|
|
|
(this.state.collapseProfiles ? 'collapsed' : '')
|
|
|
|
|
}>
|
|
|
|
|
{t('Preference profile')}
|
|
|
|
|
</h4>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<Collapse isOpen={this.state.collapseProfiles}>
|
|
|
|
@ -352,10 +354,9 @@ class Result extends Component {
|
|
|
|
|
className="badge badge-light"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundColor: grade.color,
|
|
|
|
|
color: "#fff"
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{grade.label}{" "}
|
|
|
|
|
color: '#fff',
|
|
|
|
|
}}>
|
|
|
|
|
{grade.label}{' '}
|
|
|
|
|
</span>
|
|
|
|
|
</th>
|
|
|
|
|
);
|
|
|
|
@ -367,13 +368,13 @@ class Result extends Component {
|
|
|
|
|
return (
|
|
|
|
|
<tr key={i}>
|
|
|
|
|
<td>{i + 1}</td>
|
|
|
|
|
{/*candidate.label*/}
|
|
|
|
|
{candidate.profile.map((value, i) => {
|
|
|
|
|
let percent =
|
|
|
|
|
Math.round(
|
|
|
|
|
((value * 100) / totalOfVote) * 100
|
|
|
|
|
) / 100;
|
|
|
|
|
return <td key={i}>{percent}%</td>;
|
|
|
|
|
{gradeIds.map((id, i) => {
|
|
|
|
|
const value = candidate.profile[id];
|
|
|
|
|
const percent = (
|
|
|
|
|
(value / numVotes) *
|
|
|
|
|
100
|
|
|
|
|
).toFixed(1);
|
|
|
|
|
return <td key={i}>{percent} %</td>;
|
|
|
|
|
})}
|
|
|
|
|
</tr>
|
|
|
|
|
);
|
|
|
|
@ -385,7 +386,7 @@ class Result extends Component {
|
|
|
|
|
{candidates.map((candidate, i) => {
|
|
|
|
|
return (
|
|
|
|
|
<span key={i}>
|
|
|
|
|
{i > 0 ? ", " : ""}
|
|
|
|
|
{i > 0 ? ', ' : ''}
|
|
|
|
|
<b>{i + 1}</b>: {candidate.name}
|
|
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
|