diff --git a/.gitignore b/.gitignore
index a6b1524..ca4d3c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+.tern-port
+.#*
+*.env.local
# dependencies
/node_modules
diff --git a/package.json b/package.json
index b76608a..6908f6f 100644
--- a/package.json
+++ b/package.json
@@ -41,5 +41,8 @@
"last 1 firefox version",
"last 1 safari version"
]
- }
+ },
+ "jshintConfig": {
+ "esversion": 6
+ }
}
diff --git a/public/index.html b/public/index.html
index 3522caa..03c6431 100644
--- a/public/index.html
+++ b/public/index.html
@@ -30,7 +30,7 @@
-
Platerforme de vote : Jugement Majoritaire
+ Plateforme de vote : Jugement Majoritaire
You need to enable JavaScript to run this app.
diff --git a/src/App.js b/src/App.jsx
similarity index 91%
rename from src/App.js
rename to src/App.jsx
index cf90f01..580ce64 100644
--- a/src/App.js
+++ b/src/App.jsx
@@ -1,6 +1,6 @@
import React from 'react';
import { BrowserRouter as Router} from "react-router-dom";
-import Routes from "./Routes.js";
+import Routes from "./Routes";
import Header from "./components/layouts/Header";
import Footer from "./components/layouts/Footer";
diff --git a/src/App.test.js b/src/App.test.jsx
similarity index 100%
rename from src/App.test.js
rename to src/App.test.jsx
diff --git a/src/Routes.js b/src/Routes.jsx
similarity index 61%
rename from src/Routes.js
rename to src/Routes.jsx
index 81aa54f..6b08fa7 100644
--- a/src/Routes.js
+++ b/src/Routes.jsx
@@ -1,8 +1,8 @@
import React from 'react';
import {Switch, Route } from "react-router-dom";
-import Home from "./components/views/Home.js";
-import CreateElection from "./components/views/CreateElection.js";
+import Home from "./components/views/Home";
+import CreateElection from "./components/views/CreateElection";
import Vote from "./components/views/Vote";
import Result from "./components/views/Result";
import UnknownView from "./components/views/UnknownView";
@@ -15,11 +15,11 @@ function Routes() {
-
-
-
-
-
+
+
+
+
+
diff --git a/src/Util.jsx b/src/Util.jsx
new file mode 100644
index 0000000..f4a3a17
--- /dev/null
+++ b/src/Util.jsx
@@ -0,0 +1,5 @@
+const colors = ["#015411", "#019812", "#6bca24", "#ffb200", "#ff5d00", "#b20616", "#6f0214"];
+export const grades = process.env.REACT_APP_GRADES.split(', ').map(
+ (e, i) => ({label: e, color:colors[i]}));
+
+
diff --git a/src/components/form/ButtonWithConfirm.js b/src/components/form/ButtonWithConfirm.jsx
similarity index 100%
rename from src/components/form/ButtonWithConfirm.js
rename to src/components/form/ButtonWithConfirm.jsx
diff --git a/src/components/form/HelpButton.js b/src/components/form/HelpButton.jsx
similarity index 100%
rename from src/components/form/HelpButton.js
rename to src/components/form/HelpButton.jsx
diff --git a/src/components/form/ModalConfirm.js b/src/components/form/ModalConfirm.jsx
similarity index 100%
rename from src/components/form/ModalConfirm.js
rename to src/components/form/ModalConfirm.jsx
diff --git a/src/components/layouts/Footer.js b/src/components/layouts/Footer.jsx
similarity index 100%
rename from src/components/layouts/Footer.js
rename to src/components/layouts/Footer.jsx
diff --git a/src/components/layouts/Header.js b/src/components/layouts/Header.jsx
similarity index 100%
rename from src/components/layouts/Header.js
rename to src/components/layouts/Header.jsx
diff --git a/src/components/views/CreateElection.js b/src/components/views/CreateElection.jsx
similarity index 87%
rename from src/components/views/CreateElection.js
rename to src/components/views/CreateElection.jsx
index 6f47f67..d935c12 100644
--- a/src/components/views/CreateElection.js
+++ b/src/components/views/CreateElection.jsx
@@ -1,5 +1,5 @@
import React, {Component} from "react";
-
+import { Redirect } from 'react-router-dom';
import {
Container,
Row,
@@ -13,27 +13,16 @@ import {
import {toast, ToastContainer} from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
+import { resolve } from 'url';
import HelpButton from "../form/HelpButton";
import {arrayMove, sortableContainer, sortableElement, sortableHandle} from 'react-sortable-hoc';
import ButtonWithConfirm from "../form/ButtonWithConfirm";
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {faPlus, faTrashAlt, faCheck } from '@fortawesome/free-solid-svg-icons';
+import { grades } from '../../Util';
-//TODO : variable de config dans un fichier à part (avec les mentions, le min/max de mentions, le nombre max de candidats, les maxlength,l'url api, etc ...)
-const mentions = [
- {label:"Excellent", color:"#015411"},
- {label:"Trés Bien", color:"#019812"},
- {label:"Bien", color:"#6bca24"},
- {label:"Assez Bien", color:"#ffb200"},
- {label:"Passable", color:"#ff5d00"},
- {label:"Insuffisant", color:"#b20616"},
- {label:"A Rejeter", color:"#6f0214"},
-];
-
-const PATH_API = '/api/';
-const PATH_CREATE_ELECTION = 'create';
const DragHandle = sortableHandle(({children}) => {children} );
@@ -85,10 +74,12 @@ class CreateElection extends Component {
super(props);
this.state = {
candidates:[{label:""},{label:""}],
- nbCandidatesWithLabel:0,
+ numCandidatesWithLabel:0,
title:null,
isVisibleTipsDragAndDropCandidate:true,
- nbMentions:7
+ numGrades:7,
+ successCreate: false,
+ redirectTo: null
};
this.candidateInputs = [];
this.focusInput= React.createRef();
@@ -108,7 +99,7 @@ class CreateElection extends Component {
this.setState({ candidates: candidates});
}
if(event.type === 'keypress'){
- setTimeout(()=>{ this.candidateInputs[this.state.candidates.length-1].focus()},250);
+ setTimeout(()=>{ this.candidateInputs[this.state.candidates.length-1].focus();},250);
}
};
@@ -125,15 +116,15 @@ class CreateElection extends Component {
editCandidateLabel = (event, index) => {
let candidates = this.state.candidates;
- let nbLabels = 0;
+ let numLabels = 0;
candidates[index].label = event.currentTarget.value;
candidates.map((candidate,i)=>{
if(candidate.label!==""){
- nbLabels++;
+ numLabels++;
}
return candidate.label;
});
- this.setState({candidates: candidates, nbCandidatesWithLabel:nbLabels});
+ this.setState({candidates: candidates, numCandidatesWithLabel:numLabels});
};
@@ -156,8 +147,8 @@ class CreateElection extends Component {
this.setState({candidates: candidates});
};
- handleChangeNbMentions= (event) => {
- this.setState({nbMentions: event.target.value});
+ handleChangeNumGrades= (event) => {
+ this.setState({numGrades: event.target.value});
};
componentWillMount() {
@@ -168,26 +159,35 @@ class CreateElection extends Component {
handleSubmit () {
const {
- candidates,
+ candidates,
title,
- nbMentions
+ numGrades
} = this.state;
- fetch(`${PATH_API}${PATH_CREATE_ELECTION}`, {
+
+ const endpoint = resolve(
+ process.env.REACT_APP_SERVER_URL,
+ 'election/'
+ );
+
+ fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
title: title,
- candidates: candidates,
+ candidates: candidates.map(c => c.label),
on_invitation_only: false,
- num_grades: nbMentions,
+ num_grades: numGrades,
elector_emails: []
- })
- }
- ).then(response => response.json())
- .then(result => alert(result))
- .catch(error => error);
+ })
+ })
+ .then(response => response.json())
+ .then(result => this.setState(state => ({
+ redirectTo: '/create-success/' + result.id,
+ successCreate: true
+ })))
+ .catch(error => error);
};
handleSendWithoutCandidate = () => {
@@ -197,7 +197,11 @@ class CreateElection extends Component {
};
render(){
+ const { successCreate, redirectTo } = this.state;
const params = new URLSearchParams(this.props.location.search);
+
+ if (successCreate) return ;
+
return(
@@ -244,7 +248,7 @@ class CreateElection extends Component {
Nombre de mentions :
-
+
5
6
7
@@ -257,8 +261,8 @@ class CreateElection extends Component {
- { mentions.map((mention,i) => {
- return {mention.label}
+ { grades.map((mention,i) => {
+ return {mention.label}
})
}
@@ -267,7 +271,7 @@ class CreateElection extends Component {
- {this.state.nbCandidatesWithLabel>=2?
+ {this.state.numCandidatesWithLabel>=2?
Valider
Confirmez votre vote
@@ -288,8 +292,8 @@ class CreateElection extends Component {
}
Mentions :
- { mentions.map((mention,i) => {
- return (i
{mention.label}:
+ { grades.map((mention,i) => {
+ return (i{mention.label}:
})
}
diff --git a/src/components/views/CreateSuccess.js b/src/components/views/CreateSuccess.js
index 904a94a..fbe12ca 100644
--- a/src/components/views/CreateSuccess.js
+++ b/src/components/views/CreateSuccess.js
@@ -1,18 +1,19 @@
import React, {Component} from "react";
import {Button, Col, Container, Row} from "reactstrap";
-import logoLine from "../../logos/logo-line-white.svg";
import {Link} from 'react-router-dom';
import { faCopy, faUsers } from '@fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+import logoLine from "../../logos/logo-line-white.svg";
class UnknownView extends Component {
constructor(props) {
super(props);
+ const electionSlug = this.props.match.params.handle;
this.state = {
- urlOfVote:"http://localhost/vote",
- urlOfResult:"http://localhost/result"
+ urlOfVote: "https://" + window.location.hostname + "/vote/" + electionSlug,
+ urlOfResult: "https://" + window.location.hostname + "/result/" + electionSlug
};
this.urlVoteField = React.createRef();
this.urlResultField = React.createRef();
@@ -86,7 +87,7 @@ class UnknownView extends Component {
- Participer maintenant !
+ Participer maintenant !
diff --git a/src/components/views/Home.js b/src/components/views/Home.jsx
similarity index 99%
rename from src/components/views/Home.js
rename to src/components/views/Home.jsx
index 1e3e6e1..1f184e4 100644
--- a/src/components/views/Home.js
+++ b/src/components/views/Home.jsx
@@ -54,6 +54,6 @@ class Home extends Component {
)
- }
+ };
}
export default Home;
diff --git a/src/components/views/Result.js b/src/components/views/Result.jsx
similarity index 63%
rename from src/components/views/Result.js
rename to src/components/views/Result.jsx
index de2fdad..570ed36 100644
--- a/src/components/views/Result.js
+++ b/src/components/views/Result.jsx
@@ -1,17 +1,8 @@
import React, {Component} from "react";
+import { Redirect } from 'react-router-dom';
+import { resolve } from 'url';
import {Container, Row, Col, Collapse, Card, CardHeader, CardBody, Table} from "reactstrap";
-
-//TODO : variable de config dans un fichier à part (avec les mentions, le min/max de mentions, le nombre max de candidats, les maxlength,l'url api, etc ...)
-const mentions = [
- {label: "Excellent", color: "#015411"},
- {label: "Trés Bien", color: "#019812"},
- {label: "Bien", color: "#6bca24"},
- {label: "Assez Bien", color: "#ffb200"},
- {label: "Passable", color: "#ff5d00"},
- {label: "Insuffisant", color: "#b20616"},
- {label: "A Rejeter", color: "#6f0214"},
-];
-
+import { grades } from '../../Util';
class Result extends Component {
@@ -20,49 +11,84 @@ class Result extends Component {
this.state = {
candidates: [],
title: null,
- nbMentions: 0,
+ numGrades: 0,
colSizeCandidateLg: 4,
colSizeCandidateMd: 6,
colSizeCandidateXs: 12,
- colSizeMentionLg: 1,
- colSizeMentionMd: 1,
- colSizeMentionXs: 1,
+ colSizeGradeLg: 1,
+ colSizeGradeMd: 1,
+ colSizeGradeXs: 1,
collapseGraphics: false,
- collapseProfiles: false
-
+ collapseProfiles: false,
+ redirectLost: false,
+ electionGrades: grades
};
+ }
+ handleErrors = (response) => {
+ if (!response.ok) {
+ response.json().then( response => {
+ this.setState(state => ({
+ redirectLost: '/unknown-election/' + encodeURIComponent(response)}));
+ })
+ throw Error(response);
+ }
+ return response;
}
- componentDidMount() {
- //todo fetch data from API
- let fetchedData = {
- title: "Merci d'évaluer les candidats suivants",
- candidates: [
- {id: 0, label: "Mme ABCD", mention: 2, profile: [20, 20, 20, 10, 10, 20, 0], score: "55.28"},
- {id: 2, label: "M. EFGH", mention: 3, profile: [0, 20, 20, 10, 10, 30, 10], score: "43.10"},
- {id: 3, label: "M. IJKL", mention: 4, profile: [0, 0, 20, 25, 15, 20, 20], score: "22.82"},
- {id: 4, label: "M. MNOP", mention: 4, profile: [0, 0, 15, 15, 30, 10, 30], score: "12.72"}
- ],//ordered by result
- nbMentions: 7,
- };
- let data = {
- title: fetchedData.title,
- candidates: fetchedData.candidates,
- nbMentions: fetchedData.nbMentions,
- colSizeCandidateLg: 0,
- colSizeCandidateMd: 0,
- colSizeCandidateXs: 0,
- colSizeMentionLg: Math.floor((12 - this.state.colSizeCandidateLg) / fetchedData.nbMentions),
- colSizeMentionMd: Math.floor((12 - this.state.colSizeCandidateMd) / fetchedData.nbMentions),
- colSizeMentionXs: Math.floor((12 - this.state.colSizeCandidateXs) / fetchedData.nbMentions),
- };
- data.colSizeCandidateLg = ((12 - data.colSizeMentionLg * data.nbMentions) > 0) ? (12 - data.colSizeMentionLg * data.nbMentions) : 12;
- data.colSizeCandidateMd = ((12 - data.colSizeMentionMd * data.nbMentions) > 0) ? (12 - data.colSizeMentionMd * data.nbMentions) : 12;
- data.colSizeCandidateXs = ((12 - data.colSizeMentionXs * data.nbMentions) > 0) ? (12 - data.colSizeMentionXs * data.nbMentions) : 12;
- this.setState(data);
+ resultsToState = (response) => {
+ const candidates = response.map(c => ({
+ id: c.id, label: c.name, profile: c.profile, grade:c.grade, score: c.score
+ }));
+ this.setState(state => ({candidates: candidates}));
+ return response;
}
+ detailsToState = (response) => {
+ const numGrades = response.num_grades;
+ const colSizeGradeLg = Math.floor((12 - this.state.colSizeCandidateLg) / numGrades);
+ const colSizeGradeMd = Math.floor((12 - this.state.colSizeCandidateMd) / numGrades);
+ const colSizeGradeXs = Math.floor((12 - this.state.colSizeCandidateXs) / numGrades);
+ this.setState(state => ({
+ title: response.title,
+ numGrades: numGrades,
+ colSizeGradeLg: colSizeGradeLg,
+ colSizeGradeMd: colSizeGradeMd,
+ colSizeGradeXs: colSizeGradeXs,
+ colSizeCandidateLg: ((12 - colSizeGradeLg * numGrades) > 0) ? (12 - colSizeGradeLg * numGrades) : 12,
+ colSizeCandidateMd: ((12 - colSizeGradeMd * numGrades) > 0) ? (12 - colSizeGradeMd * numGrades) : 12,
+ colSizeCandidateXs: ((12 - colSizeGradeXs * numGrades) > 0) ? (12 - colSizeGradeXs * numGrades) : 12,
+ electionGrades: grades.slice(0, numGrades)
+ }));
+ return response;
+ }
+
+ componentDidMount() {
+ // FIXME we should better handling logs
+
+ const electionSlug = this.props.match.params.handle;
+
+ // get details of the election
+ const detailsEndpoint = resolve(process.env.REACT_APP_SERVER_URL,
+ 'election/get/'.concat(electionSlug));
+
+ fetch(detailsEndpoint)
+ .then(this.handleErrors)
+ .then(response => response.json())
+ .then(this.detailsToState)
+ .catch(error => console.log(error));
+
+ // get results of the election
+ const resultsEndpoint = resolve(process.env.REACT_APP_SERVER_URL,
+ 'election/results/'.concat(electionSlug));
+
+ fetch(resultsEndpoint)
+ .then(this.handleErrors)
+ .then(response => response.json())
+ .then(this.resultsToState)
+ .catch(error => console.log(error));
+
+ }
toggleGraphics = () => {
this.setState(state => ({collapseGraphics: !state.collapseGraphics}));
@@ -73,6 +99,16 @@ class Result extends Component {
};
render() {
+
+ const { redirectLost,
+ candidates,
+ electionGrades
+ } = this.state;
+
+ if (redirectLost) {
+ return ()
+ }
+
return (
@@ -81,13 +117,13 @@ class Result extends Component {
Résultat du vote :
- {this.state.candidates.map((candidate, i) => {
+ {candidates.map((candidate, i) => {
return ({candidate.label}{candidate.score}% {mentions[candidate.mention].label} );
+ }}>{grades[candidate.grade].label});
})}
@@ -102,9 +138,9 @@ class Result extends Component {
+ style={{height: (candidates.length * 28) + 30}}/>
- {this.state.candidates.map((candidate, i) => {
+ {candidates.map((candidate, i) => {
return (
{i + 1}
{/*candidate.label*/}
@@ -120,7 +156,7 @@ class Result extends Component {
}
return ( );
}else{
return null
@@ -136,7 +172,7 @@ class Result extends Component {
- {this.state.candidates.map((candidate, i) => {
+ {candidates.map((candidate, i) => {
return (
{(i > 0) ? ", " : ""}{i + 1} : {candidate.label} );
})}
@@ -144,14 +180,13 @@ class Result extends Component {
- {mentions.map((mention, i) => {
- return (i < this.state.nbMentions) ?
+ {electionGrades.map((grade, i) => {
+ return (
{mention.label} :
- })
- }
+ }}>{grade.label}
+ )})}
@@ -173,14 +208,14 @@ class Result extends Component {
#
- {mentions.map((mention, i) => {
+ {electionGrades.map((grade, i) => {
return ({mention.label} );
+ }}>{grade.label} );
})}
- {this.state.candidates.map((candidate, i) => {
+ {candidates.map((candidate, i) => {
return (
{i + 1}
{/*candidate.label*/}
@@ -191,7 +226,7 @@ class Result extends Component {
})}
- {this.state.candidates.map((candidate, i) => {
+ {candidates.map((candidate, i) => {
return ({(i > 0) ? ", " : ""}{i + 1} : {candidate.label} );
})}
diff --git a/src/components/views/UnknownView.js b/src/components/views/UnknownView.jsx
similarity index 100%
rename from src/components/views/UnknownView.js
rename to src/components/views/UnknownView.jsx
diff --git a/src/components/views/Vote.js b/src/components/views/Vote.js
deleted file mode 100644
index ac84fe6..0000000
--- a/src/components/views/Vote.js
+++ /dev/null
@@ -1,160 +0,0 @@
-import React, {Component} from "react";
-import {Button, Col, Container, Row} from "reactstrap";
-import {toast, ToastContainer} from "react-toastify";
-import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
-import {faCheck} from "@fortawesome/free-solid-svg-icons";
-
-//TODO : variable de config dans un fichier à part (avec les mentions, le min/max de mentions, le nombre max de candidats, les maxlength,l'url api, etc ...)
-const mentions = [
- {label:"Excellent", color:"#015411"},
- {label:"Trés Bien", color:"#019812"},
- {label:"Bien", color:"#6bca24"},
- {label:"Assez Bien", color:"#ffb200"},
- {label:"Passable", color:"#ff5d00"},
- {label:"Insuffisant", color:"#b20616"},
- {label:"A Rejeter", color:"#6f0214"},
- ];
-
-
-
-class Vote extends Component {
-
- constructor(props) {
- super(props);
- this.state = {
- candidates:[],
- title:null,
- nbMentions:0,
- ratedCandidates:[],
- colSizeCandidateLg:4,
- colSizeCandidateMd:6,
- colSizeCandidateXs:12,
- colSizeMentionLg:1,
- colSizeMentionMd:1,
- colSizeMentionXs:1,
-
- };
-
- }
-
- componentDidMount() {
- //todo fetch data from API
- let fetchedData={
- title:"Merci d'évaluer les candidats suivants",
- candidates:[ {id:0, label:"Mme ABCD"}, {id:2, label:"M. EFGH"}, {id:3, label:"M. IJKL"}, {id:4, label:"M. MNOP"} ],
- nbMentions:7,
- };
- let data={
- title:fetchedData.title,
- candidates:fetchedData.candidates,
- nbMentions:fetchedData.nbMentions,
- colSizeCandidateLg:0,
- colSizeCandidateMd:0,
- colSizeCandidateXs:0,
- colSizeMentionLg:Math.floor((12-this.state.colSizeCandidateLg)/fetchedData.nbMentions),
- colSizeMentionMd:Math.floor((12-this.state.colSizeCandidateMd)/fetchedData.nbMentions),
- colSizeMentionXs:Math.floor((12-this.state.colSizeCandidateXs)/fetchedData.nbMentions),
- };
- data.colSizeCandidateLg=((12-data.colSizeMentionLg*data.nbMentions)>0)?(12-data.colSizeMentionLg*data.nbMentions):12;
- data.colSizeCandidateMd=((12-data.colSizeMentionMd*data.nbMentions)>0)?(12-data.colSizeMentionMd*data.nbMentions):12;
- data.colSizeCandidateXs=((12-data.colSizeMentionXs*data.nbMentions)>0)?(12-data.colSizeMentionXs*data.nbMentions):12;
-
- //shuffle candidates
- let i,
- j,
- temp;
- for (i = data.candidates.length - 1; i > 0; i--) {
- j = Math.floor(Math.random() * (i + 1));
- temp = data.candidates[i];
- data.candidates[i] = data.candidates[j];
- data.candidates[j] = temp;
- }
- this.setState(data);
- }
-
-
- handleMentionClick= (event) => {
- let data={
- id:parseInt(event.currentTarget.getAttribute("data-id")),
- value:parseInt(event.currentTarget.value)
- };
- //remove candidate
- let ratedCandidates = this.state.ratedCandidates.filter(ratedCandidate => ratedCandidate.id !== data.id);
- ratedCandidates.push(data);
- this.setState({ratedCandidates:ratedCandidates});
-
- };
-
- handleSubmitWithoutAllRate = () => {
- toast.error("Vous devez évaluer l'ensemble des propositions/candidats !", {
- position: toast.POSITION.TOP_CENTER
- });
- };
- handleSubmit= (event) => {
- event.preventDefault();
- };
-
-
- render(){
- return(
-
-
-
-
- )
- }
-}
-export default Vote;
diff --git a/src/components/views/Vote.jsx b/src/components/views/Vote.jsx
new file mode 100644
index 0000000..690a1ce
--- /dev/null
+++ b/src/components/views/Vote.jsx
@@ -0,0 +1,213 @@
+import React, {Component} from "react";
+import { Redirect } from 'react-router-dom';
+import {Button, Col, Container, Row} from "reactstrap";
+import {toast, ToastContainer} from "react-toastify";
+import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
+import {faCheck} from "@fortawesome/free-solid-svg-icons";
+import { resolve } from 'url';
+import { grades } from '../../Util';
+
+
+class Vote extends Component {
+
+ constructor(props) {
+ super(props);
+ this.state = {
+ candidates:[],
+ title:null,
+ numGrades:0,
+ ratedCandidates:[],
+ colSizeCandidateLg:4,
+ colSizeCandidateMd:6,
+ colSizeCandidateXs:12,
+ colSizeGradeLg:1,
+ colSizeGradeMd:1,
+ colSizeGradeXs:1,
+ redirectTo: null,
+ electionGrades: grades
+ };
+
+ }
+
+ handleErrors = (response) => {
+ if (!response.ok) {
+ response.json().then( response => {
+ console.log(response);
+ this.setState(state => ({
+ redirectTo: '/unknown-election/' + encodeURIComponent(response)}));
+ })
+ throw Error(response);
+ }
+ return response;
+ }
+
+ detailsToState = (response) => {
+ const numGrades = response.num_grades;
+ const candidates = response.candidates.map((c, i) => ({
+ id: i,
+ label: c
+ }));
+ //shuffle candidates
+ let i, j, temp;
+ for (i = candidates.length - 1; i > 0; i--) {
+ j = Math.floor(Math.random() * (i + 1));
+ temp = candidates[i];
+ candidates[i] = candidates[j];
+ candidates[j] = temp;
+ }
+ const colSizeGradeLg = Math.floor((12 - this.state.colSizeCandidateLg) / numGrades);
+ const colSizeGradeMd = Math.floor((12 - this.state.colSizeCandidateMd) / numGrades);
+ const colSizeGradeXs = Math.floor((12 - this.state.colSizeCandidateXs) / numGrades);
+
+ this.setState(state => ({
+ title: response.title,
+ candidates: candidates,
+ numGrades: numGrades,
+ colSizeGradeLg: colSizeGradeLg,
+ colSizeGradeMd: colSizeGradeMd,
+ colSizeGradeXs: colSizeGradeXs,
+ colSizeCandidateLg: ((12 - colSizeGradeLg * numGrades) > 0) ?
+ (12 - colSizeGradeLg * numGrades) : 12,
+ colSizeCandidateMd: ((12 - colSizeGradeMd * numGrades) > 0) ?
+ (12 - colSizeGradeMd * numGrades) : 12,
+ colSizeCandidateXs: ((12 - colSizeGradeXs * numGrades) > 0) ?
+ (12 - colSizeGradeXs * numGrades) : 12,
+ electionGrades: grades.slice(0, numGrades)
+ }));
+ return response;
+ }
+
+ componentDidMount() {
+ // FIXME we should better handling logs
+
+ const electionSlug = this.props.match.params.handle;
+ const detailsEndpoint = resolve(process.env.REACT_APP_SERVER_URL,
+ 'election/get/'.concat(electionSlug));
+
+ fetch(detailsEndpoint)
+ .then(this.handleErrors)
+ .then(response => response.json())
+ .then(this.detailsToState)
+ .catch(error => console.log(error));
+ }
+
+
+ handleGradeClick = (event) => {
+ let data={
+ id:parseInt(event.currentTarget.getAttribute("data-id")),
+ value:parseInt(event.currentTarget.value)
+ };
+ //remove candidate
+ let ratedCandidates = this.state.ratedCandidates.filter(ratedCandidate => ratedCandidate.id !== data.id);
+ ratedCandidates.push(data);
+ this.setState({ratedCandidates:ratedCandidates});
+
+ };
+
+ handleSubmitWithoutAllRate = () => {
+ toast.error("Vous devez évaluer l'ensemble des propositions/candidats !", {
+ position: toast.POSITION.TOP_CENTER
+ });
+ };
+
+ handleSubmit = (event) => {
+ event.preventDefault();
+
+ const { ratedCandidates } = this.state;
+ const electionSlug = this.props.match.params.handle;
+ const endpoint = resolve(process.env.REACT_APP_SERVER_URL,
+ 'election/vote/');
+
+ const gradesById = {};
+ ratedCandidates.forEach(c => { gradesById[c.id] = c.value; });
+ const gradesByCandidate = [];
+ Object.keys(gradesById)
+ .sort()
+ .forEach(id => {gradesByCandidate.push(gradesById[id]);});
+
+ fetch(endpoint, {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify({
+ election: electionSlug,
+ grades_by_candidate: gradesByCandidate,
+ })
+ })
+ .then(this.handleErrors)
+ .then(result => this.setState({redirectTo: '/vote-success/' + electionSlug}))
+ .catch(error => error);
+ };
+
+
+ render(){
+
+ const { redirectTo,
+ candidates,
+ electionGrades
+ } = this.state;
+
+ if (redirectTo) {
+ return ();
+ }
+
+ return(
+
+
+
+
+ )
+ }
+}
+export default Vote;
diff --git a/src/components/views/VoteSuccess.js b/src/components/views/VoteSuccess.js
index 9b07532..2e74211 100644
--- a/src/components/views/VoteSuccess.js
+++ b/src/components/views/VoteSuccess.js
@@ -1,26 +1,23 @@
import React, {Component} from "react";
-import {Button, Col, Container, Row} from "reactstrap";
+import {Col, Container, Row} from "reactstrap";
import logoLine from "../../logos/logo-line-white.svg";
import {Link} from 'react-router-dom';
-import { faCopy, faUsers } from '@fortawesome/free-solid-svg-icons';
-import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
class UnknownView extends Component {
- constructor(props) {
- super(props);
- }
-
render(){
return(
-
+
+
+
- Participation enregistrée avec succès !
- Merci pour votre participation.
+
+ Participation enregistrée avec succès !
+ Merci pour votre participation.
diff --git a/src/index.js b/src/index.jsx
similarity index 100%
rename from src/index.js
rename to src/index.jsx
diff --git a/src/serviceWorker.js b/src/serviceWorker.jsx
similarity index 100%
rename from src/serviceWorker.js
rename to src/serviceWorker.jsx