Merge pull request #44 from MieuxVoter/feature/results-only-at-end

Feature/results only at end
pull/73/head
guhur 4 years ago committed by GitHub
commit 3bcde1e39a

@ -0,0 +1,32 @@
{
"env": {
"browser": true,
"es6": true
},
"parser": "babel-eslint",
"extends": [
"eslint:recommended",
"plugin:react/recommended"
],
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": [
"react"
],
"settings": {
"react": {
"version": "detect"
}
},
"rules": {
}
}

@ -70,11 +70,11 @@ module.exports = {
);
if (count > 0) {
console.log(
/* console.log(
`i18next-scanner: count=${chalk.cyan(count)}, file=${chalk.yellow(
JSON.stringify(file.relative)
)}`
);
);*/
}
done();

@ -10,10 +10,10 @@
"@fortawesome/free-solid-svg-icons": "^5.9.0",
"@fortawesome/react-fontawesome": "^0.1.4",
"@svgr/webpack": "4.1.0",
"@typescript-eslint/eslint-plugin": "1.6.0",
"@typescript-eslint/parser": "1.6.0",
"@typescript-eslint/eslint-plugin": "2.29.0",
"@typescript-eslint/parser": "2.29.0",
"axios": "^0.19.0",
"babel-eslint": "10.0.1",
"babel-eslint": "^10.1.0",
"babel-jest": "^24.8.0",
"babel-loader": "8.0.5",
"babel-plugin-named-asset-import": "^0.3.2",
@ -26,14 +26,14 @@
"dotenv-expand": "4.2.0",
"enzyme": "^3.9.0",
"enzyme-adapter-react-16": "^1.13.1",
"eslint": "^5.16.0",
"eslint-config-react-app": "^4.0.1",
"eslint-loader": "2.1.2",
"eslint-plugin-flowtype": "2.50.1",
"eslint-plugin-import": "2.16.0",
"eslint-plugin-jsx-a11y": "6.2.1",
"eslint-plugin-react": "7.12.4",
"eslint-plugin-react-hooks": "^1.5.0",
"eslint": "^6.8.0",
"eslint-config-react-app": "^5.2.1",
"eslint-loader": "4.0.2",
"eslint-plugin-flowtype": "4.7.0",
"eslint-plugin-import": "2.20.2",
"eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-react": "7.19.0",
"eslint-plugin-react-hooks": "^3.0.0",
"file-loader": "3.0.1",
"fs-extra": "7.0.1",
"global": "^4.4.0",
@ -56,6 +56,7 @@
"postcss-normalize": "7.0.1",
"postcss-preset-env": "6.6.0",
"postcss-safe-parser": "4.0.1",
"prop-types": "^15.7.2",
"query-string": "^6.12.0",
"querystringify": "^2.0.0",
"react": "^16.8.6",

@ -19,12 +19,12 @@
"Candidates/Proposals": "Kandidaten/Abstimmungsvorschlag ",
"Add a proposal": "Einen Abstimmungsvorschlag hinzufügen",
"Advanced options": "Weitere Optionen",
"Starting date:": "Anfangsdatum:",
"Ending date: ": "Enddatum: ",
"Grades:": "Note",
"Starting date": "Anfangsdatum",
"Ending date": "Enddatum",
"Grades": "Note",
"You can select here the number of grades for your election": " Sie können hier die Anzahl der Noten für Ihre Wahl auswählen ",
"5 = Excellent, Very good, Good, Fair, Passable": "5 = hervorragend, sehr gut, gut, befriedigend, ausreichend",
"Participants:": "Teilnehmer :",
"Participants": "Teilnehmer",
"Add here participants' emails": "Fügen Sie hier die Email Adressen der Teilnehmer hinzu.",
"List voters' emails in case the election is not opened": "Falls die Wahl noch nicht sofort geöffnet werden soll, fügen Sie die Email Adressen der Teilnehmer hier zu.",
"Validate": "Ok",
@ -60,7 +60,6 @@
"The election will take place from": "Die Wahl findet von",
"at": "um",
"to": "bis",
"Grades": "Note",
"Voters' list": "Wählerliste",
"Voters received a link to vote by email. Each link can be used only once!": "Die Wähler erhielten per E-Mail einen Link zur Stimmabgabe. Jeder Link kann nur einmal verwendet werden!",
"Results of the election:": "Ergebnisse der Wahl",
@ -73,5 +72,11 @@
"The election is over. You can't vote anymore": "Die Wahl ist vorbei. Sie können nicht mehr wählen",
"You need a token to vote in this election": "Sie brauchen eine Wertmarke, um an dieser Wahl teilzunehmen",
"You seem to have already voted.": "Sie scheinen bereits abgestimmt zu haben.",
"The parameters of the election are incorrect.": "Die Parameter der Wahl sind falsch."
"The parameters of the election are incorrect.": "Die Parameter der Wahl sind falsch.",
"Access to results" : "Zugang zu den Ergebnissen",
"Immediately": "Sofort",
"At the end of the election": "Am Ende der Wahl",
"The results page will not be accessible until all participants have voted.":"Die Ergebnisseite wird nicht zugänglich sein, bis alle Teilnehmer abgestimmt haben.",
"The results page will not be accessible until the end date is reached.": "Die Ergebnisseite wird nicht zugänglich sein, bis das Enddatum erreicht ist.",
"No one will be able to see the result until the end date is reached or until all participants have voted.": "Niemand wird das Ergebnis sehen können, bis das Enddatum erreicht ist oder bis alle Teilnehmer abgestimmt haben."
}

@ -19,12 +19,12 @@
"Candidates/Proposals": "Candidates/Proposals",
"Add a proposal": "Add a proposal",
"Advanced options": "Advanced options",
"Starting date:": "Starting date:",
"Ending date: ": "Ending date: ",
"Grades:": "Grades:",
"Starting date": "Starting date",
"Ending date": "Ending date",
"Grades": "Grades",
"You can select here the number of grades for your election": "You can select here the number of grades for your election",
"5 = Excellent, Very good, Good, Fair, Passable": "5 = Excellent, Very good, Good, Fair, Passable",
"Participants:": "Participants:",
"Participants": "Participants",
"Add here participants' emails": "Add here participants' emails",
"List voters' emails in case the election is not opened": "List voters' emails in case the election is not opened",
"Validate": "Validate",
@ -62,7 +62,6 @@
"The election will take place from": "The election will take place from",
"at": "at",
"to": "to",
"Grades": "Grades",
"Voters' list": "Voters' list",
"Graph": "Graph",
"Preference profile": "Merit profile",
@ -80,5 +79,11 @@
"The parameters of the election are incorrect.": "The parameters of the election are incorrect.",
"Support us !": "Support us !",
"PayPal - The safer, easier way to pay online!": "PayPal - The safer, easier way to pay online!",
"Number of votes:": "Number of votes:"
"Number of votes:": "Number of votes:",
"Access to results" : "Access to results",
"Immediately": "Immediately",
"At the end of the election": "At the end of the election",
"The results page will not be accessible until all participants have voted.":"The results page will not be accessible until all participants have voted.",
"The results page will not be accessible until the end date is reached.": "The results page will not be accessible until the end date is reached.",
"No one will be able to see the result until the end date is reached or until all participants have voted.": "No one will be able to see the result until the end date is reached or until all participants have voted."
}

@ -19,9 +19,9 @@
"Candidates/Proposals": "Candidatos(as)/Propuestas",
"Add a proposal": "Añadir una propuesta",
"Advanced options": "Opciones avanzadas",
"Starting date:": "Fecha de inicio:",
"Ending date: ": "Fecha de finalización: ",
"Grades:": "Escala",
"Starting date": "Fecha de inicio",
"Ending date": "Fecha de finalización",
"Grades": "Escala",
"You can select here the number of grades for your election": "Puede seleccionar aquí el número de niveles de la escala para su elección",
"5 = Excellent, Very good, Good, Fair, Passable": "5 == Excelente, Muy bien, Bien, Regular, Pasable",
"Participants:": "Participantes",
@ -62,7 +62,6 @@
"The election will take place from": "La elección tendrá lugar desde",
"at": "a las",
"to": "hasta",
"Grades": "Escala",
"Voters' list": "Lista de votantes",
"Graph": "Gráfico",
"Preference profile": "Perfil de preferencia",
@ -81,5 +80,11 @@
"The election is over. You can't vote anymore": "La elección ha terminado. Ya no puedes votar.",
"You need a token to vote in this election": "Necesitas una ficha para votar en esta elección",
"You seem to have already voted.": "Parece que ya has votado.",
"The parameters of the election are incorrect.": "Los parámetros de la elección son incorrectos."
"The parameters of the election are incorrect.": "Los parámetros de la elección son incorrectos.",
"Access to results" : "Acceso a los resultados",
"Immediately": "Inmediatamente",
"At the end of the election": "Al final de la elección",
"The results page will not be accessible until all participants have voted.":"La página de resultados no será accesible hasta que todos los participantes hayan votado.",
"The results page will not be accessible until the end date is reached.": "No se podrá acceder a la página de resultados hasta que se alcance la fecha de finalización.",
"No one will be able to see the result until the end date is reached or until all participants have voted.": "Nadie podrá ver el resultado hasta que se alcance la fecha final o hasta que todos los participantes hayan votado."
}

@ -19,19 +19,18 @@
"Candidates/Proposals": "Candidats/Propositions",
"Add a proposal": "Ajouter une proposition",
"Advanced options": "Options avancées",
"Starting date:": "Date de début :",
"Ending date: ": "Date de fin : ",
"Ending date:": "Date de fin : ",
"Grades:": "Mentions",
"Starting date": "Date de début",
"Ending date": "Date de fin ",
"Grades": "Mentions",
"You can select here the number of grades for your election": "You pouvez choisir ici le nombre de mentions de votre élection",
"5 = Excellent, Very good, Good, Fair, Passable": "5 = Excellent, Très bien, Bien, Assez bien, Passable",
"Participants:": "Participants :",
"Participants": "Participants",
"Add here participants' emails": "Ajouter ici les emails des participants",
"List voters' emails in case the election is not opened": "Lister ici les emails des électeurs dans le cas où l'élection n'est pas ouverte.",
"Validate": "Valider",
"Confirm your vote": "Confirmer votre vote",
"The form contains no address.": "Aucune adresse email n'a été ajoutée.",
"The election will be opened to anyone with the link": "L'élection sera accessible à tous ceux qui disposent de ce lien",
"The election will be opened to anyone with the link": "L'élection sera accessible à tous ceux qui disposent du lien",
"Start the election": "Démarrer l'élection",
"Cancel": "Annuler",
"Confirm": "Valider",
@ -62,7 +61,6 @@
"The election will take place from": "Le vote se déroulera du",
"at": "à",
"to": "au",
"Grades": "Mentions",
"Voters' list": "Listes des électeurs",
"Graph": "Graphique",
"Preference profile": "Profil de mérites",
@ -80,5 +78,11 @@
"The parameters of the election are incorrect.": "Les paramètres de l'élection sont incorrects.",
"Support us !": "Soutenez-nous !",
"PayPal - The safer, easier way to pay online!": "PayPal - Le moyen le plus sûr et le plus simple de payer en ligne !",
"Number of votes:": "Nombre de votes :"
"Number of votes:": "Nombre de votes :",
"Access to results" : "Accès aux résultats",
"Immediately": "Immédiatement",
"At the end of the election": "A la fin de l'élection",
"The results page will not be accessible until all participants have voted.":"La page de résultats ne sera pas accessible tant que tous les participants n'auront pas voté.",
"The results page will not be accessible until the end date is reached.": "La page de résultats ne sera pas accessible tant que la date de fin ne sera pas atteinte.",
"No one will be able to see the result until the end date is reached or until all participants have voted.": "Personne ne pourra voir le résultat tant que la date de fin n'est pas atteinte ou que tous les participants n'ont pas voté."
}

@ -19,9 +19,9 @@
"Candidates/Proposals": "Кандидаты/Предложения",
"Add a proposal": "Добавьте предложение",
"Advanced options": "Расширенные настройки",
"Starting date:": "Дата начала:",
"Ending date: ": "Дата окончания: ",
"Grades:": "Оценки:",
"Starting date": "Дата начала",
"Ending date": "Дата окончания",
"Grades": "Оценки",
"You can select here the number of grades for your election": "Здесь вы можете выбрать количество оценок для вашего голосования",
"5 = Excellent, Very good, Good, Fair, Passable": "5 = Отлично, Очень хорошо, Хорошо, Удовлетворительно, Допустимо",
"Participants:": "Участники:",
@ -62,7 +62,6 @@
"The election will take place from": "Голосование состоится",
"at": "в",
"to": "к",
"Grades": "Оценки",
"Voters' list": "Список проголосовавших",
"Graph": "Кривая",
"Preference profile": "Профиль заслуг",
@ -80,5 +79,11 @@
"The parameters of the election are incorrect.": "Параметры голосвания неверны.",
"Support us !": "Поддержите нас !",
"PayPal - The safer, easier way to pay online!": "PayPal - Безопасный и простой способ платить онлайн!",
"Number of votes:": "Количество голосов:"
"Number of votes:": "Количество голосов:",
"Access to results" : "Доступ к результатам",
"Immediately": "Немедленно",
"At the end of the election": "По окончании выборов",
"The results page will not be accessible until all participants have voted.":"Страница результатов не будет доступна до тех пор, пока все участники не проголосуют.",
"The results page will not be accessible until the end date is reached.": "Страница результатов не будет доступна до тех пор, пока не будет достигнута конечная дата.",
"No one will be able to see the result until the end date is reached or until all participants have voted.": "Никто не сможет увидеть результат до тех пор, пока не будет достигнут конечный срок или пока все участники не проголосуют."
}

@ -1,19 +1,19 @@
import React from 'react';
import React from "react";
import Routes from './Routes';
import Header from './components/layouts/Header';
import Footer from './components/layouts/Footer';
import AppContextProvider from './AppContext';
import Routes from "./Routes";
import Header from "./components/layouts/Header";
import Footer from "./components/layouts/Footer";
import AppContextProvider from "./AppContext";
function App() {
return (
<AppContextProvider>
<div>
<Header />
<Routes />
<Footer />
</div>
</AppContextProvider>
<AppContextProvider>
<div>
<Header />
<Routes />
<Footer />
</div>
</AppContextProvider>
);
}

@ -1,12 +1,13 @@
/* eslint react/prop-types: 0 */
import React, { createContext, Suspense } from "react";
import {BrowserRouter as Router} from 'react-router-dom';
import Loader from './components/loader';
import { BrowserRouter as Router } from "react-router-dom";
import Loader from "./components/loader";
export const AppContext = createContext();
const AppContextProvider = ({ children }) => {
const defaultState = {
// eslint-disable-next-line no-undef
urlServer: process.env.REACT_APP_SERVER_URL,
routesServer: {
setElection: "election/",
@ -16,12 +17,12 @@ const AppContextProvider = ({ children }) => {
}
};
return (
<Suspense fallback={<Loader/>} >
<Router>
<AppContext.Provider value={defaultState}>
{children}
</AppContext.Provider>
</Router>
<Suspense fallback={<Loader />}>
<Router>
<AppContext.Provider value={defaultState}>
{children}
</AppContext.Provider>
</Router>
</Suspense>
);
};

@ -1,3 +1,4 @@
/* eslint react/prop-types: 0 */
import React from "react";
import { Container, Row, Col } from "reactstrap";
import { Link } from "react-router-dom";
@ -13,7 +14,7 @@ export const UNKNOWN_TOKEN_ERROR = "E7";
export const USED_TOKEN_ERROR = "E8";
export const WRONG_ELECTION_ERROR = "E9";
export const redirectError = (errorMsg, history) => {};
export const redirectError = () => {};
export const errorMessage = (error, t) => {
if (error.startsWith(UNKNOWN_ELECTION_ERROR)) {

@ -10,7 +10,6 @@ import UnknownElection from "./components/views/UnknownElection";
import CreateSuccess from "./components/views/CreateSuccess";
import VoteSuccess from "./components/views/VoteSuccess";
function Routes() {
return (
<main className="d-flex flex-column justify-content-center">
@ -19,8 +18,18 @@ function Routes() {
<Route path="/create-election" component={CreateElection} />
<Route path="/vote/:slug" component={Vote} />
<Route path="/result/:slug" component={Result} />
<Route path="/link/:slug" component={props => <CreateSuccess invitationOnly={true} {...props} />} />
<Route path="/links/:slug" component={props => <CreateSuccess invitationOnly={false} {...props} />} />
<Route
path="/link/:slug"
component={props => (
<CreateSuccess invitationOnly={true} {...props} />
)}
/>
<Route
path="/links/:slug"
component={props => (
<CreateSuccess invitationOnly={false} {...props} />
)}
/>
<Route path="/vote-success/:slug" component={VoteSuccess} />
<Route path="/unknown-election/:slug" component={UnknownElection} />
<Route component={UnknownView} />

@ -1,34 +1,26 @@
import i18n from './i18n.jsx';
import i18n from "./i18n.jsx";
const colors = [
'#015411',
'#019812',
'#6bca24',
'#ffb200',
'#ff5d00',
'#b20616',
'#6f0214',
"#015411",
"#019812",
"#6bca24",
"#ffb200",
"#ff5d00",
"#b20616",
"#6f0214"
];
const gradeNames = [
'Excellent',
'Very good',
'Good',
'Fair',
'Passable',
'Insufficient',
'To reject',
"Excellent",
"Very good",
"Good",
"Fair",
"Passable",
"Insufficient",
"To reject"
];
const gradeValues = [
6,
5,
4,
3,
2,
1,
0
];
const gradeValues = [6, 5, 4, 3, 2, 1, 0];
export const grades = gradeNames.map((name, i) => ({
label: name,

@ -1,7 +1,7 @@
import React from 'react';
import {Button} from 'reactstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
/* eslint react/prop-types: 0 */
import React from "react";
import { Button } from "reactstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const CopyField = props => {
const ref = React.createRef();
@ -9,14 +9,14 @@ const CopyField = props => {
event.target.focus();
event.target.select();
};
const handleClickOnButton = event => {
const handleClickOnButton = () => {
const input = ref.current;
input.focus();
input.select();
document.execCommand('copy');
document.execCommand("copy");
};
const {t, value, icon} = props;
const { t, value, icon } = props;
return (
<div className="input-group ">
@ -33,9 +33,10 @@ const CopyField = props => {
<Button
className="btn btn-outline-light"
onClick={handleClickOnButton}
type="button">
type="button"
>
<FontAwesomeIcon icon={icon} className="mr-2" />
{t('Copy')}
{t("Copy")}
</Button>
</div>
</div>

@ -1,17 +1,24 @@
import React from 'react';
import i18n from '../../i18n'
/* eslint react/prop-types: 0 */
import React from "react";
import i18n from "../../i18n";
const Helloasso = props => {
const locale =
i18n.language.substring(0, 2).toLowerCase() === "fr" ? "fr" : "en";
const linkHelloAssoBanner =
locale === "fr"
? "https://www.helloasso.com/associations/mieux-voter/formulaires/1/widget"
: "https://www.helloasso.com/associations/mieux-voter/formulaires/1/widget/en";
const Helloasso = (props) => {
const locale=i18n.language.substring(0,2).toLowerCase()==="fr"?"fr":"en";
const linkHelloAssoBanner=(locale==="fr")?"https://www.helloasso.com/associations/mieux-voter/formulaires/1/widget":"https://www.helloasso.com/associations/mieux-voter/formulaires/1/widget/en";
return (
<a href={linkHelloAssoBanner} target="_blank" rel="noopener noreferrer">
<img src={"/banner/"+locale+"/helloasso.png"} alt="support us on helloasso" style={{width:props.width}} />
</a>);
return (
<a href={linkHelloAssoBanner} target="_blank" rel="noopener noreferrer">
<img
src={"/banner/" + locale + "/helloasso.png"}
alt="support us on helloasso"
style={{ width: props.width }}
/>
</a>
);
};
export default Helloasso;
export default Helloasso;

@ -1,29 +1,44 @@
import React from 'react';
import i18n from '../../i18n'
import {withTranslation} from 'react-i18next';
/* eslint react/prop-types: 0 */
import React from "react";
import i18n from "../../i18n";
import { withTranslation } from "react-i18next";
import { faPaypal } from "@fortawesome/free-brands-svg-icons";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
const Paypal = props => {
const { t } = props;
let localeStringShort = i18n.language.substring(0, 2);
let localeStringComplete =
localeStringShort.toLowerCase() + "_" + localeStringShort.toUpperCase();
if (localeStringComplete === "en_EN") {
localeStringComplete = "en_US";
}
const pixelLink =
"https://www.paypal.com/" + localeStringComplete + "/i/scr/pixel.gif";
const Paypal = (props) => {
const {t} = props;
let localeStringShort=i18n.language.substring(0,2);
let localeStringComplete=localeStringShort.toLowerCase()+"_"+localeStringShort.toUpperCase();
if(localeStringComplete==="en_EN"){
localeStringComplete="en_US";
}
const pixelLink="https://www.paypal.com/"+localeStringComplete+"/i/scr/pixel.gif";
return (
<div className="d-inline-block m-auto">
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top">
<button type="submit" className={"btn "+props.btnColor} title={t("PayPal - The safer, easier way to pay online!")} > <FontAwesomeIcon icon={faPaypal} className="mr-2" />{t("Support us !")}</button>
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="KB2Z7L9KARS7C" />
<img alt="" border="0" src={pixelLink} width="1" height="1" />
</form></div>);
return (
<div className="d-inline-block m-auto">
<form
action="https://www.paypal.com/cgi-bin/webscr"
method="post"
target="_top"
>
<button
type="submit"
className={"btn " + props.btnColor}
title={t("PayPal - The safer, easier way to pay online!")}
>
{" "}
<FontAwesomeIcon icon={faPaypal} className="mr-2" />
{t("Support us !")}
</button>
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="KB2Z7L9KARS7C" />
<img alt="" border="0" src={pixelLink} width="1" height="1" />
</form>
</div>
);
};
export default withTranslation()(Paypal);
export default withTranslation()(Paypal);

@ -1,3 +1,4 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import ModalConfirm from "./ModalConfirm";

@ -1,3 +1,4 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
@ -25,7 +26,7 @@ class HelpButton extends Component {
render() {
return (
<span>
<span className={this.props.className}>
<span>
{this.state.tooltipOpen ? (
<span

@ -1,3 +1,4 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";

@ -1,3 +1,4 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { withTranslation } from "react-i18next";
import { Link } from "react-router-dom";
@ -13,29 +14,29 @@ class Footer extends Component {
const linkStyle = { whiteSpace: "nowrap" };
const { t } = this.props;
return (
<footer className="text-center" >
<footer className="text-center">
<div>
<Link to="/" style={linkStyle}>
{t("Homepage")}
</Link>
{" "}<span>-</span>{" "}
<a
href="https://github.com/MieuxVoter"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
{t("Source code")}
</a>
{" "}<span>-</span>{" "}
<a
href="https://mieuxvoter.fr/"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
{t("Who are we?")}
</a>
<Link to="/" style={linkStyle}>
{t("Homepage")}
</Link>
<span className="m-2">-</span>
<a
href="https://github.com/MieuxVoter"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
{t("Source code")}
</a>
<span className="m-2">-</span>
<a
href="https://mieuxvoter.fr/"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
{t("Who are we?")}
</a>
</div>
<div className="mt-3">
<Paypal btnColor="btn-primary" />

@ -1,8 +1,8 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { Collapse, Navbar, NavbarToggler, Nav, NavItem } from "reactstrap";
import { Link } from "react-router-dom";
import { withTranslation } from 'react-i18next';
import { withTranslation } from "react-i18next";
import logo from "../../logos/logo-color.svg";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@ -10,15 +10,15 @@ import { faRocket } from "@fortawesome/free-solid-svg-icons";
import LanguageSelector from "./LanguageSelector";
class Header extends Component {
state = {
isOpen: false
};
state = {
isOpen: false
};
toggle = () => {
this.setState({
isOpen: !this.state.isOpen
});
}
};
render() {
const { t } = this.props;
@ -33,7 +33,7 @@ class Header extends Component {
<div className="align-self-center ml-2">
<div className="logo-text">
<h1>
{t("Voting platform")}
{t("Voting platform")}
<small>{t("Majority Judgment")}</small>
</h1>
</div>
@ -49,7 +49,7 @@ class Header extends Component {
{t("Start an election")}
</Link>
</NavItem>
<NavItem style={{width:"150px"}}>
<NavItem style={{ width: "150px" }}>
<LanguageSelector />
</NavItem>
</Nav>

@ -1,30 +1,30 @@
import React from 'react';
import ReactFlagsSelect from 'react-flags-select';
import 'react-flags-select/css/react-flags-select.css';
import i18n from '../../i18n'
/* eslint react/prop-types: 0 */
import React from "react";
import ReactFlagsSelect from "react-flags-select";
import "react-flags-select/css/react-flags-select.css";
import i18n from "../../i18n";
const LanguageSelector = () => {
const selectHandler = (e) => {
let locale=e.toLowerCase();
if(locale==="gb")locale="en";
i18n.changeLanguage(locale);
};
let locale=i18n.language.substring(0,2).toUpperCase();
if(locale==="EN")locale="GB";
return (<ReactFlagsSelect onSelect={selectHandler}
countries={["GB", "FR", "ES", "DE", "RU"]}
showOptionLabel={false}
defaultCountry={locale}
selectedSize={15}
optionsSize={22}
showSelectedLabel={false}
/>);
const selectHandler = e => {
let locale = e.toLowerCase();
if (locale === "gb") locale = "en";
i18n.changeLanguage(locale);
};
let locale = i18n.language.substring(0, 2).toUpperCase();
if (locale === "EN") locale = "GB";
return (
<ReactFlagsSelect
onSelect={selectHandler}
countries={["GB", "FR", "ES", "DE", "RU"]}
showOptionLabel={false}
defaultCountry={locale}
selectedSize={15}
optionsSize={22}
showSelectedLabel={false}
/>
);
};
export default LanguageSelector;

@ -1,13 +1,14 @@
import React from 'react';
import logo from './loader-pulse-2.gif';
import './style.css';
/* eslint react/prop-types: 0 */
import React from "react";
import logo from "./loader-pulse-2.gif";
import "./style.css";
const Loader = () => {
return (
<div className="loader bg-primary">
<img src={logo} alt="Loading..." />
</div>);
return (
<div className="loader bg-primary">
<img src={logo} alt="Loading..." />
</div>
);
};
export default Loader;

File diff suppressed because it is too large Load Diff

@ -1,58 +1,54 @@
import React, {Component} from 'react';
import {Col, Container, Row} from 'reactstrap';
import {Link} from 'react-router-dom';
import {withTranslation, Trans} from 'react-i18next';
import {faCopy, faUsers, faExclamationTriangle} from '@fortawesome/free-solid-svg-icons';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import logoLine from '../../logos/logo-line-white.svg';
import {AppContext} from '../../AppContext';
import CopyField from '../CopyField';
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { Col, Container, Row } from "reactstrap";
import { Link } from "react-router-dom";
import { withTranslation, Trans } from "react-i18next";
import {
faCopy,
faUsers,
faExclamationTriangle
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import logoLine from "../../logos/logo-line-white.svg";
import { AppContext } from "../../AppContext";
import CopyField from "../CopyField";
class CreateSuccess extends Component {
static contextType = AppContext;
constructor(props) {
super(props);
console.log(props);
const electionSlug = this.props.match.params.slug;
this.state = {
urlOfVote:
window.location.origin + '/vote/' + electionSlug,
urlOfResult:
window.location.origin + '/result/' + electionSlug,
urlOfVote: window.location.origin + "/vote/" + electionSlug,
urlOfResult: window.location.origin + "/result/" + electionSlug
};
this.urlVoteField = React.createRef();
this.urlResultField = React.createRef();
}
handleClickOnCopyResult = event => {
handleClickOnCopyResult = () => {
const input = this.urlResultField.current;
input.focus();
input.select();
document.execCommand('copy');
document.execCommand("copy");
};
render() {
const {t} = this.props;
console.log(this.props)
const { t } = this.props;
const electionLink = this.props.invitationOnly ? (
<>
<p className="mt-4 mb-1">
{t('Voters received a link to vote by email. Each link can be used only once!')}
{t(
"Voters received a link to vote by email. Each link can be used only once!"
)}
</p>
</>
) : (
<>
<p className="mt-4 mb-1">
{t('You can now share the election link to participants:')}
{t("You can now share the election link to participants:")}
</p>
<CopyField
value={this.state.urlOfVote}
icon={faCopy}
t={t}
/>
<CopyField value={this.state.urlOfVote} icon={faCopy} t={t} />
</>
);
@ -65,27 +61,25 @@ class CreateSuccess extends Component {
</Row>
<Row className="mt-4">
<Col className="text-center offset-lg-3" lg="6">
<h2>{t('Successful election creation!')}</h2>
<h2>{t("Successful election creation!")}</h2>
{electionLink}
<p className="mt-4 mb-1">
{t('Here is the link for the results in real time:')}
{t("Here is the link for the results in real time:")}
</p>
<CopyField
value={this.state.urlOfResult}
icon={faCopy}
t={t}
/>
<CopyField value={this.state.urlOfResult} icon={faCopy} t={t} />
</Col>
</Row>
<Row className="mt-4 mb-4">
<Col className="text-center offset-lg-3" lg="6">
<div className=" bg-danger text-white p-2 ">
<h4 className="m-0 p-0 text-center">
<FontAwesomeIcon icon={faExclamationTriangle} className="mr-2" />
{t('Keep these links carefully')}
<FontAwesomeIcon
icon={faExclamationTriangle}
className="mr-2"
/>
{t("Keep these links carefully")}
</h4>
<p className="small m-2 p-0">
<Trans i18nKey="t">
@ -101,10 +95,11 @@ class CreateSuccess extends Component {
<Row className="mt-4 mb-4">
<Col className="text-center">
<Link
to={'/vote/' + this.props.match.params.slug}
className="btn btn-success">
to={"/vote/" + this.props.match.params.slug}
className="btn btn-success"
>
<FontAwesomeIcon icon={faUsers} className="mr-2" />
{t('Participate now!')}
{t("Participate now!")}
</Link>
</Col>
</Row>

@ -1,14 +1,13 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { withTranslation } from 'react-i18next';
import { withTranslation } from "react-i18next";
import { Container, Row, Col, Button, Input } from "reactstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faRocket } from "@fortawesome/free-solid-svg-icons";
import { Redirect } from "react-router-dom";
import logoLine from "../../logos/logo-line-white.svg";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
@ -29,7 +28,7 @@ class Home extends Component {
};
render() {
const {t} = this.props;
const { t } = this.props;
const redirect = this.state.redirect;
if (redirect) {
@ -53,7 +52,9 @@ class Home extends Component {
<Row>
<Col className="text-center">
<h3>
{t("Simple and free: organize an election with Majority Judgment.")}
{t(
"Simple and free: organize an election with Majority Judgment."
)}
</h3>
</Col>
</Row>
@ -77,7 +78,7 @@ class Home extends Component {
className="btn btn-block btn-secondary mt-2"
>
<FontAwesomeIcon icon={faRocket} className="mr-2" />
{t("Start")}
{t("Start")}
</Button>
</Col>
</Row>

@ -1,6 +1,7 @@
import React, {Component} from 'react';
import {withTranslation} from 'react-i18next';
import {resolve} from 'url';
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { withTranslation } from "react-i18next";
import { resolve } from "url";
import {
Container,
Row,
@ -9,12 +10,11 @@ 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";
class Result extends Component {
static contextType = AppContext;
@ -34,15 +34,15 @@ class Result extends Component {
collapseGraphics: false,
collapseProfiles: false,
electionGrades: i18nGrades(),
errorMessage: '',
errorMessage: ""
};
}
handleErrors = response => {
if (!response.ok) {
response.json().then(response => {
this.setState(state => ({
errorMessage: errorMessage(response, this.props.t),
this.setState(() => ({
errorMessage: errorMessage(response, this.props.t)
}));
});
throw Error(response);
@ -55,24 +55,24 @@ class Result extends Component {
id: c.id,
name: c.name,
profile: c.profile,
grade: c.grade,
grade: c.grade
}));
this.setState(state => ({candidates: candidates}));
this.setState(() => ({ 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 => ({
this.setState(() => ({
title: response.title,
numGrades: numGrades,
colSizeGradeLg: colSizeGradeLg,
@ -90,7 +90,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;
};
@ -98,38 +98,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});
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)
@ -142,9 +142,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)
@ -156,21 +156,20 @@ 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 i18nGradesObject = i18nGrades();
const offsetGrade = i18nGradesObject.length-(this.state.numGrades);
const offsetGrade = i18nGradesObject.length - this.state.numGrades;
if (errorMessage && errorMessage !== '') {
if (errorMessage && errorMessage !== "") {
return <Error value={errorMessage} />;
}
@ -181,7 +180,6 @@ class Result extends Component {
candidates && candidates.length > 0
? Object.keys(candidates[0].profile)
: [];
console.log(gradeIds);
return (
<Container>
@ -193,26 +191,29 @@ class Result extends Component {
<Row className="mt-5">
<Col>
<h1>{t('Results of the election:')}</h1>
<h1>{t("Results of the election:")}</h1>
<h5>
<small>
{t('Number of votes:')}
{' ' + numVotes}
{t("Number of votes:")}
{" " + numVotes}
</small>
</h5>
<hr className="mb-5" />
<ol>
{candidates.map((candidate, i) => {
const gradeValue=candidate.grade+offsetGrade;
const gradeValue = candidate.grade + offsetGrade;
return (
<li key={i} className="mt-2">
<span className="mt-2 ml-2">{candidate.name}</span>
<span
className="badge badge-light ml-2 mt-2"
style={{
backgroundColor: electionGrades.slice(0).reverse()[(candidate.grade)].color,
color: '#fff',
}}>
backgroundColor: electionGrades.slice(0).reverse()[
candidate.grade
].color,
color: "#fff"
}}
>
{i18nGradesObject.slice(0).reverse()[gradeValue].label}
</span>
{/* <span className="badge badge-dark mt-2 ml-2">
@ -231,10 +232,11 @@ 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,42 +244,46 @@ 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>
{gradeIds.slice(0).reverse().map((id, i) => {
const value = candidate.profile[id];
if (value > 0) {
let percent =
(value * 100) / numVotes + '%';
if (i === 0) {
percent = 'auto';
{gradeIds
.slice(0)
.reverse()
.map((id, i) => {
const value = candidate.profile[id];
if (value > 0) {
let percent =
(value * 100) / numVotes + "%";
if (i === 0) {
percent = "auto";
}
return (
<td
key={i}
style={{
width: percent,
backgroundColor: this.state
.electionGrades[i].color
}}
>
&nbsp;
</td>
);
} else {
return null;
}
return (
<td
key={i}
style={{
width: percent,
backgroundColor: this.state
.electionGrades[i].color,
}}>
&nbsp;
</td>
);
} else {
return null;
}
})}
})}
</tr>
</tbody>
</table>
@ -293,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>
);
@ -309,8 +315,9 @@ class Result extends Component {
className="badge badge-light mr-2 mt-2"
style={{
backgroundColor: grade.color,
color: '#fff',
}}>
color: "#fff"
}}
>
{grade.label}
</span>
);
@ -328,10 +335,11 @@ 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}>
@ -348,9 +356,10 @@ class Result extends Component {
className="badge badge-light"
style={{
backgroundColor: grade.color,
color: '#fff',
}}>
{grade.label}{' '}
color: "#fff"
}}
>
{grade.label}{" "}
</span>
</th>
);
@ -362,14 +371,17 @@ class Result extends Component {
return (
<tr key={i}>
<td>{i + 1}</td>
{gradeIds.slice(0).reverse().map((id, i) => {
const value = candidate.profile[id];
const percent = (
(value / numVotes) *
100
).toFixed(1);
return <td key={i}>{percent} %</td>;
})}
{gradeIds
.slice(0)
.reverse()
.map((id, i) => {
const value = candidate.profile[id];
const percent = (
(value / numVotes) *
100
).toFixed(1);
return <td key={i}>{percent} %</td>;
})}
</tr>
);
})}
@ -380,7 +392,7 @@ class Result extends Component {
{candidates.map((candidate, i) => {
return (
<span key={i}>
{i > 0 ? ', ' : ''}
{i > 0 ? ", " : ""}
<b>{i + 1}</b>: {candidate.name}
</span>
);

@ -1,3 +1,4 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { Col, Container, Row } from "reactstrap";
import { withTranslation } from "react-i18next";
@ -13,7 +14,7 @@ class UnknownElection extends Component {
}
render() {
const {t} = this.props;
const { t } = this.props;
return (
<Container>
<Row>
@ -23,14 +24,18 @@ class UnknownElection extends Component {
</Row>
<Row className="mt-4">
<Col className="text-center">
<h2>{t("Oops! This election does not exist or it is not available anymore.")}</h2>
<h2>
{t(
"Oops! This election does not exist or it is not available anymore."
)}
</h2>
<p>{t("You can start another election.")}</p>
</Col>
</Row>
<Row className="mt-4">
<Col className="text-center">
<Link to="/" className="btn btn-secondary">
{t("Go back to homepage")}
{t("Go back to homepage")}
</Link>
</Col>
</Row>

@ -1,3 +1,4 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { Col, Container, Row } from "reactstrap";
import logoLine from "../../logos/logo-line-white.svg";
@ -21,13 +22,13 @@ class UnknownView extends Component {
</Row>
<Row className="mt-4">
<Col className="text-center">
<h2>Oups ! Cette page n'existe pas</h2>
<h2>Ooops ! this page doesn&#39;t exist !</h2>
</Col>
</Row>
<Row className="mt-4">
<Col className="text-center">
<Link to="/" className="btn btn-secondary">
Revenir à l'accueil
Go back to homepage
</Link>
</Col>
</Row>

@ -1,14 +1,15 @@
import React, {Component} from 'react';
import {Redirect} from 'react-router-dom';
import {withTranslation} from 'react-i18next';
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 {i18nGrades} from '../../Util';
import {AppContext} from '../../AppContext';
import {errorMessage} from '../../Errors';
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { Redirect } from "react-router-dom";
import { withTranslation } from "react-i18next";
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 { i18nGrades } from "../../Util";
import { AppContext } from "../../AppContext";
import { errorMessage } from "../../Errors";
const shuffle = array => array.sort(() => Math.random() - 0.5);
@ -29,7 +30,7 @@ class Vote extends Component {
colSizeGradeXs: 1,
redirectTo: null,
electionGrades: i18nGrades(),
errorMsg: "",
errorMsg: ""
};
}
@ -37,9 +38,9 @@ class Vote extends Component {
if (!response.ok) {
response.json().then(response => {
console.log(response);
const {t} = this.props;
this.setState(state => ({
errorMsg: errorMessage(response, t)
const { t } = this.props;
this.setState(() => ({
errorMsg: errorMessage(response, t)
}));
});
throw Error(response);
@ -51,21 +52,21 @@ class Vote extends Component {
const numGrades = response.num_grades;
const candidates = response.candidates.map((c, i) => ({
id: i,
label: c,
label: c
}));
shuffle(candidates);
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 => ({
this.setState(() => ({
title: response.title,
candidates: candidates,
numGrades: numGrades,
@ -83,7 +84,7 @@ class Vote extends Component {
colSizeCandidateXs:
12 - colSizeGradeXs * numGrades > 0
? 12 - colSizeGradeXs * numGrades
: 12,
: 12
}));
return response;
};
@ -94,9 +95,9 @@ class Vote extends Component {
const detailsEndpoint = resolve(
this.context.urlServer,
this.context.routesServer.getElection.replace(
new RegExp(':slug', 'g'),
electionSlug,
),
new RegExp(":slug", "g"),
electionSlug
)
);
fetch(detailsEndpoint)
.then(this.handleErrors)
@ -107,33 +108,33 @@ class Vote extends Component {
handleGradeClick = event => {
let data = {
id: parseInt(event.currentTarget.getAttribute('data-id')),
value: parseInt(event.currentTarget.value),
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,
ratedCandidate => ratedCandidate.id !== data.id
);
ratedCandidates.push(data);
this.setState({ratedCandidates});
this.setState({ ratedCandidates });
};
handleSubmitWithoutAllRate = () => {
const {t} = this.props;
toast.error(t('You have to judge every candidate/proposal!'), {
position: toast.POSITION.TOP_CENTER,
const { t } = this.props;
toast.error(t("You have to judge every candidate/proposal!"), {
position: toast.POSITION.TOP_CENTER
});
};
handleSubmit = event => {
event.preventDefault();
const {ratedCandidates} = this.state;
const { ratedCandidates } = this.state;
const electionSlug = this.props.match.params.slug;
const token = this.props.location.search.substr(7);
const endpoint = resolve(
this.context.urlServer,
this.context.routesServer.voteElection,
this.context.routesServer.voteElection
);
const gradesById = {};
@ -147,29 +148,29 @@ class Vote extends Component {
const payload = {
election: electionSlug,
grades_by_candidate: gradesByCandidate,
grades_by_candidate: gradesByCandidate
};
if (token !== '') {
payload['token'] = token;
if (token !== "") {
payload["token"] = token;
}
fetch(endpoint, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload),
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(payload)
})
.then(this.handleErrors)
.then(result =>
this.setState({redirectTo: '/vote-success/' + electionSlug}),
.then(() =>
this.setState({ redirectTo: "/vote-success/" + electionSlug })
)
.catch(error => error);
};
render() {
const {t} = this.props;
const {candidates, errorMsg, redirectTo} = this.state;
const { t } = this.props;
const { candidates, errorMsg, redirectTo } = this.state;
const grades = i18nGrades();
const offsetGrade = grades.length-this.state.numGrades;
const offsetGrade = grades.length - this.state.numGrades;
const electionGrades = grades.slice(0, this.state.numGrades);
if (redirectTo) {
@ -177,15 +178,15 @@ class Vote extends Component {
}
if (errorMsg !== "") {
return(
<Container>
return (
<Container>
<Row>
<Col>
<h3>{errorMsg}</h3>
</Col>
</Row>
</Container>
);
</Container>
);
}
return (
@ -201,7 +202,8 @@ class Vote extends Component {
<Col
xs={this.state.colSizeCandidateXs}
md={this.state.colSizeCandidateMd}
lg={this.state.colSizeCandidateLg}>
lg={this.state.colSizeCandidateLg}
>
<h5>&nbsp;</h5>
</Col>
{electionGrades.map((grade, gradeId) => {
@ -212,10 +214,12 @@ class Vote extends Component {
lg={this.state.colSizeGradeLg}
key={gradeId}
className="text-center p-0"
style={{lineHeight: 2}}>
style={{ lineHeight: 2 }}
>
<small
className="nowrap bold badge"
style={{backgroundColor: grade.color, color: '#fff'}}>
style={{ backgroundColor: grade.color, color: "#fff" }}
>
{grade.label}
</small>
</Col>
@ -229,81 +233,95 @@ class Vote extends Component {
<Col
xs={this.state.colSizeCandidateXs}
md={this.state.colSizeCandidateMd}
lg={this.state.colSizeCandidateLg}>
lg={this.state.colSizeCandidateLg}
>
<h5 className="m-0">{candidate.label}</h5>
<hr className="d-lg-none" />
</Col>
{electionGrades.map((grade, gradeId) => {
console.assert(gradeId < this.state.numGrades)
const gradeValue = grade.value-offsetGrade;
console.assert(gradeId < this.state.numGrades);
const gradeValue = grade.value - offsetGrade;
return (
<Col
xs={this.state.colSizeGradeXs}
md={this.state.colSizeGradeMd}
lg={this.state.colSizeGradeLg}
key={gradeId}
className="text-lg-center">
className="text-lg-center"
>
<label
htmlFor={'candidateGrade' + candidateId + '-' + gradeValue}
className="check">
htmlFor={
"candidateGrade" + candidateId + "-" + gradeValue
}
className="check"
>
<small
className="nowrap d-lg-none ml-2 bold badge"
style={
this.state.ratedCandidates.find(function(
ratedCandidat,
ratedCandidat
) {
return (
JSON.stringify(ratedCandidat) ===
JSON.stringify({id: candidate.id, value: gradeValue})
JSON.stringify({
id: candidate.id,
value: gradeValue
})
);
})
? {backgroundColor: grade.color, color: '#fff'}
? { backgroundColor: grade.color, color: "#fff" }
: {
backgroundColor: 'transparent',
color: '#000',
backgroundColor: "transparent",
color: "#000"
}
}>
}
>
{grade.label}
</small>
<input
type="radio"
name={'candidate' + candidateId}
id={'candidateGrade' + candidateId + '-' + gradeValue}
name={"candidate" + candidateId}
id={"candidateGrade" + candidateId + "-" + gradeValue}
data-index={candidateId}
data-id={candidate.id}
value={grade.value-offsetGrade}
value={grade.value - offsetGrade}
onClick={this.handleGradeClick}
defaultChecked={this.state.ratedCandidates.find(
function(element) {
return (
JSON.stringify(element) ===
JSON.stringify({id: candidate.id, value: gradeValue})
JSON.stringify({
id: candidate.id,
value: gradeValue
})
);
},
}
)}
/>
<span
className="checkmark"
style={
this.state.ratedCandidates.find(function(
ratedCandidat,
ratedCandidat
) {
return (
JSON.stringify(ratedCandidat) ===
JSON.stringify({id: candidate.id, value: gradeValue})
JSON.stringify({
id: candidate.id,
value: gradeValue
})
);
})
? {backgroundColor: grade.color, color: '#fff'}
? { backgroundColor: grade.color, color: "#fff" }
: {
backgroundColor: 'transparent',
color: '#000',
backgroundColor: "transparent",
color: "#000"
}
}
/>
</label>
</Col>
)
);
})}
</Row>
);
@ -316,14 +334,15 @@ class Vote extends Component {
<Button
type="button"
onClick={this.handleSubmitWithoutAllRate}
className="btn btn-dark ">
className="btn btn-dark "
>
<FontAwesomeIcon icon={faCheck} className="mr-2" />
{t('Validate')}
{t("Validate")}
</Button>
) : (
<Button type="submit" className="btn btn-success ">
<FontAwesomeIcon icon={faCheck} className="mr-2" />
{t('Validate')}
{t("Validate")}
</Button>
)}
</Col>

@ -1,6 +1,7 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { Col, Container, Row } from "reactstrap";
import {withTranslation} from 'react-i18next';
import { withTranslation } from "react-i18next";
import logoLine from "../../logos/logo-line-white.svg";
import { Link } from "react-router-dom";
import { AppContext } from "../../AppContext";
@ -9,7 +10,7 @@ import Paypal from "../banner/Paypal";
class VoteSuccess extends Component {
static contextType = AppContext;
render() {
const {t} = this.props;
const { t } = this.props;
return (
<Container>
<Row>
@ -21,16 +22,16 @@ class VoteSuccess extends Component {
<Col className="text-center offset-lg-3" lg="6">
<h2>{t("Your participation was recorded with success!")}</h2>
<p>{t("Thanks for your participation.")}</p>
<div className="mt-3">
<Paypal btnColor="btn-success"/>
</div>
<div className="mt-3">
<Paypal btnColor="btn-success" />
</div>
</Col>
</Row>
<Row className="mt-4">
<Col className="text-center">
<Link to="/" className="btn btn-secondary">
{t("Go back to homepage")}
{t("Go back to homepage")}
</Link>
</Col>
</Row>

@ -1,9 +1,8 @@
import React from 'react';
import Loader from '../loader';
import React from "react";
import Loader from "../loader";
const Wait = () => {
return (<Loader />);
return <Loader />;
};
export default Wait;

@ -1,4 +1,4 @@
import React from "react";
/* eslint react/prop-types: 0 */
export const UNKNOWN_ELECTION_ERROR = "E1";
export const ONGOING_ELECTION_ERROR = "E2";
@ -10,7 +10,7 @@ export const UNKNOWN_TOKEN_ERROR = "E7";
export const USED_TOKEN_ERROR = "E8";
export const WRONG_ELECTION_ERROR = "E9";
export const redirectError = (errorMsg, history) => {};
export const redirectError = () => {};
export const errorMessage = (error, t) => {
if (error.startsWith(UNKNOWN_ELECTION_ERROR)) {

@ -1,29 +1,28 @@
import i18n from 'i18next';
import XHR from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';
import i18n from "i18next";
import XHR from "i18next-xhr-backend";
import LanguageDetector from "i18next-browser-languagedetector";
import { initReactI18next } from "react-i18next";
i18n
.use(XHR)
.use(LanguageDetector)
.use(initReactI18next) // bind react-i18next to the instance
.init({
fallbackLng: 'en',
fallbackLng: "en",
debug: true,
saveMissing: true, // send not translated keys to endpoint
defaultValue: "__STRING_NOT_TRANSLATED__",
keySeparator: '>',
nsSeparator: '|',
backend: {
loadPath: '/locale/i18n/{{lng}}/resource.json',
// path to post missing resources
},
keySeparator: ">",
nsSeparator: "|",
backend: {
loadPath: "/locale/i18n/{{lng}}/resource.json"
// path to post missing resources
},
interpolation: {
escapeValue: false, // not needed for react!!
},
escapeValue: false // not needed for react!!
}
// react i18next special options (optional)
// override if needed - omit if ok with defaults
@ -39,5 +38,4 @@ i18n
*/
});
export default i18n;

@ -154,6 +154,10 @@ li.sortable {
border-radius: 50%;
}
.checkround.checkround-gray {
border-color: $gray-600;
}
/* When the radio button is checked, add a blue background */
.radio input:checked ~ .checkround {
background-color: #fff;
@ -181,6 +185,10 @@ li.sortable {
background: $mv-blue-color;
}
/*.radio .checkround.checkround-gray:after {
background: $gray-600;
}*/
/* The check */
.check {
display: block;
@ -311,22 +319,21 @@ li.sortable {
padding: 0.25em !important;
}
/** flag selector **/
.flag-select > button {
height:35px;
height: 35px;
}
.flag-select__options{
width:65px;
text-align:center;
.flag-select__options {
width: 65px;
text-align: center;
background-color: $mv-light-color !important;
}
.flag-select__options .flag-select__option{
padding:0;
margin:0;
.flag-select__options .flag-select__option {
padding: 0;
margin: 0;
}
.flag-select__options .flag-select__option__icon{
top:0;
.flag-select__options .flag-select__option__icon {
top: 0;
}

@ -21,8 +21,10 @@ const isLocalhost = Boolean(
);
export function register(config) {
// eslint-disable-next-line no-undef
if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) {
// The URL constructor is available in all browsers that support SW.
// eslint-disable-next-line no-undef
const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href);
if (publicUrl.origin !== window.location.origin) {
// Our service worker won't work if PUBLIC_URL is on a different origin
@ -32,6 +34,7 @@ export function register(config) {
}
window.addEventListener("load", () => {
// eslint-disable-next-line no-undef
const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
if (isLocalhost) {

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save