fix: prettier with new config

pull/89/head
Pierre-Louis Guhur 1 year ago
parent be07a76b04
commit 15183fe3d4

@ -1,8 +1,8 @@
import {UncontrolledAlert} from 'reactstrap';
import {useTranslation} from "next-i18next";
import { UncontrolledAlert } from 'reactstrap';
import { useTranslation } from 'next-i18next';
const AlertDismissible = ({msg, color}) => {
const {t} = useTranslation();
const AlertDismissible = ({ msg, color }) => {
const { t } = useTranslation();
if (msg) {
return (
@ -13,18 +13,17 @@ const AlertDismissible = ({msg, color}) => {
href={`mailto:${CONTACT_MAIL}?subject=[HELP]`}
className="btn btn-success m-auto"
>
{t("error.help")}
{t('error.help')}
</a>
</p>
</UncontrolledAlert >
</UncontrolledAlert>
);
}
return null;
}
};
AlertDismissible.defaultProps = {
color: 'danger'
color: 'danger',
};
export default AlertDismissible;

@ -2,12 +2,17 @@ import React from 'react';
// import Plot from 'react-plotly.js';
function Bulles(props) {
// récupération des résultats de l'élection et stockage en tableau
const votesBrut = (Object.values(props))[0];
const votesBrut = Object.values(props)[0];
// déclaration et initialisation des mentions et couleurs
const mentionsBrut = ['Passable', 'Assez bien', 'Bien', 'Très bien', 'Excellent'];
const mentionsBrut = [
'Passable',
'Assez bien',
'Bien',
'Très bien',
'Excellent',
];
const couleursBrut = ['#BB9C42', '#AABA44', '#DCDF44', '#B3D849', '#61AD45'];
//----------- Traitement des données -----------//
@ -37,12 +42,11 @@ function Bulles(props) {
nvTab[0] = 100;
for (i = 1; i < tab.length; i++) {
nvTab[i] = ((1 + ((tab[i] / tab[(i - 1)]) / 40)) * nvTab[(i - 1)]);
nvTab[i] = (1 + tab[i] / tab[i - 1] / 40) * nvTab[i - 1];
}
return nvTab;
}
// déclaration de l'objet votes-mention et votes-couleur
var votesMentionNonOrdonnes = {};
var votesCouleurNonOrdonnes = {};
@ -58,15 +62,21 @@ function Bulles(props) {
var votesCouleurOrdonnes = inverse(votesCouleurNonOrdonnes);
// vérification du nombre de votes classés par ordre croissant et passés initialement en propriétés au composant
console.log("Les données transmises au composant concernant le nombre de votes par mention sont : ");
console.log(
'Les données transmises au composant concernant le nombre de votes par mention sont : '
);
console.log(votesBrut);
// vérification des mentions destinées à être associées aux votes et ordonnées initialement par ordre mélioratif
console.log("Les mentions des votes sont classées initialement par ordre mélioratif de la façon suivante :");
console.log(
'Les mentions des votes sont classées initialement par ordre mélioratif de la façon suivante :'
);
console.log(mentionsBrut);
// vérification du nombre de votes classés par ordre croissant
console.log("Les mentions-votes classées par ordre croissant de votes sont : ");
console.log(
'Les mentions-votes classées par ordre croissant de votes sont : '
);
console.log(votesMentionOrdonnes);
// séparation des mentions et des votes
@ -75,38 +85,40 @@ function Bulles(props) {
const couleurs = Object.keys(votesCouleurOrdonnes);
// vérification des mentions et des votes prêts à être traités pour la représentation graphique
console.log('La liste des mentions issue du classement par ordre croissant de votes est :');
console.log(
'La liste des mentions issue du classement par ordre croissant de votes est :'
);
console.log(mentions);
console.log('La liste du nombre de votes correspondant, classée par ordre croissant, est :');
console.log(
'La liste du nombre de votes correspondant, classée par ordre croissant, est :'
);
console.log(votes);
// déclaration et initialisation des rayons de bulle pour la représentation graphique
var rayons = [];
rayons = redAmpli(votes)
rayons = redAmpli(votes);
// vérification des rayons
console.log('La liste des rayons à représenter graphiquement est la suivante :');
console.log(
'La liste des rayons à représenter graphiquement est la suivante :'
);
console.log(rayons);
// déclaration et initialisation des textes des bulles
const texteBulle1 = (mentions[0] + "<br>" + votes[0] + " votes").toString();
const texteBulle2 = (mentions[1] + "<br>" + votes[1] + " votes").toString();
const texteBulle3 = (mentions[2] + "<br>" + votes[2] + " votes").toString();
const texteBulle4 = (mentions[3] + "<br>" + votes[3] + " votes").toString();
const texteBulle5 = (mentions[4] + "<br>" + votes[4] + " votes").toString();
const texteBulle1 = (mentions[0] + '<br>' + votes[0] + ' votes').toString();
const texteBulle2 = (mentions[1] + '<br>' + votes[1] + ' votes').toString();
const texteBulle3 = (mentions[2] + '<br>' + votes[2] + ' votes').toString();
const texteBulle4 = (mentions[3] + '<br>' + votes[3] + ' votes').toString();
const texteBulle5 = (mentions[4] + '<br>' + votes[4] + ' votes').toString();
//---------------------------------------------//
//----------- Affichage des données -----------//
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
setTimeout(() => setLoading(false), 3000);
})
});
return (
<div>TBD</div>
// <div>
@ -156,8 +168,8 @@ function Bulles(props) {
// ) : (
// <LoadingScreen />
// )}
// </div>
)
// </div>
);
}
export default Bulles;

@ -1,6 +1,6 @@
import {IconProp} from "@fortawesome/fontawesome-svg-core";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {Row, Col, Button} from "reactstrap";
import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Row, Col, Button } from 'reactstrap';
interface ButtonProps {
children?: React.ReactNode;
@ -8,35 +8,37 @@ interface ButtonProps {
position?: 'left' | 'right';
[props: string]: any;
}
const ButtonWithIcon = ({icon, children, position = 'left', ...props}: ButtonProps) => {
const ButtonWithIcon = ({
icon,
children,
position = 'left',
...props
}: ButtonProps) => {
if (icon && position === 'left') {
return <Button {...props}>
<Row className='gx-2 align-items-end'>
<Col className='col-auto'>
<FontAwesomeIcon icon={icon} />
</Col>
<Col className='col-auto'>
{children}
</Col>
</Row>
</Button>
return (
<Button {...props}>
<Row className="gx-2 align-items-end">
<Col className="col-auto">
<FontAwesomeIcon icon={icon} />
</Col>
<Col className="col-auto">{children}</Col>
</Row>
</Button>
);
} else if (icon && position === 'right') {
return (
<Button {...props}>
<Row className="gx-2 align-items-end">
<Col className="col-auto">{children}</Col>
<Col className="col-auto">
<FontAwesomeIcon icon={icon} />
</Col>
</Row>
</Button>
);
} else {
return <Button {...props}>{children}</Button>;
}
else if (icon && position === 'right') {
return <Button {...props}>
<Row className='gx-2 align-items-end'>
<Col className='col-auto'>
{children}
</Col>
<Col className='col-auto'>
<FontAwesomeIcon icon={icon} />
</Col>
</Row>
</Button>
}
else {
return (<Button {...props}>{children}</Button>)
}
}
};
export default ButtonWithIcon
export default ButtonWithIcon;

@ -1,5 +1,5 @@
import * as React from "react";
import * as d3 from "d3";
import * as React from 'react';
import * as d3 from 'd3';
function drawChart(svgRef: React.RefObject<SVGSVGElement>) {
const data = [12, 5, 6, 6, 9, 10];
@ -8,21 +8,21 @@ function drawChart(svgRef: React.RefObject<SVGSVGElement>) {
const svg = d3.select(svgRef.current);
svg
.attr("width", w)
.attr("height", h)
.style("margin-top", 50)
.style("margin-left", 50);
.attr('width', w)
.attr('height', h)
.style('margin-top', 50)
.style('margin-left', 50);
svg
.selectAll("rect")
.selectAll('rect')
.data(data)
.enter()
.append("rect")
.attr("x", (d, i) => i * 40)
.attr("y", (d, i) => h - 10 * d)
.attr("width", 20)
.attr("height", (d, i) => d * 10)
.attr("fill", "steelblue");
.append('rect')
.attr('x', (d, i) => i * 40)
.attr('y', (d, i) => h - 10 * d)
.attr('width', 20)
.attr('height', (d, i) => d * 10)
.attr('fill', 'steelblue');
}
const Chart: React.FunctionComponent = () => {

@ -2,20 +2,16 @@ import React, { useRef, useState, useEffect } from 'react';
import D3Chart from './D3Chart';
const ChartWrapper = () => {
const chartArea = useRef(null);
const [chart, setChart] = useState(null);
const chartArea = useRef(null);
const [chart, setChart] = useState(null);
useEffect(() => {
if (!chart) {
setChart(new D3Chart(chartArea.current));
}
}, [chart]);
useEffect(() => {
if (!chart) {
setChart(new D3Chart(chartArea.current));
}
}, [chart]);
return <div ref={chartArea}></div>;
};
return (
<div ref={chartArea}></div>
);
}
export default ChartWrapper;
export default ChartWrapper;

@ -1,13 +1,13 @@
/* eslint react/prop-types: 0 */
import React from "react";
import { Button, UncontrolledTooltip } from "reactstrap";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from 'react';
import { Button, UncontrolledTooltip } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faClone,
faVoteYea,
faExclamationTriangle,
faExternalLinkAlt,
} from "@fortawesome/free-solid-svg-icons";
} from '@fortawesome/free-solid-svg-icons';
import ClipboardJS from 'clipboard';
const CopyField = (props) => {
const ref = React.createRef();
@ -17,17 +17,15 @@ const CopyField = (props) => {
};
const { t, value, iconCopy, text } = props;
if (typeof window !== "undefined") {
if (typeof window !== 'undefined') {
new ClipboardJS('.btn');
}
return (
<div className="input-group my-4 ">
<input
type="text"
style={{ display: "none" }}
style={{ display: 'none' }}
className="form-control"
ref={ref}
value={value}
@ -36,7 +34,6 @@ const CopyField = (props) => {
/>
<div className="input-group-append copy">
{/* <Button
href={value}
target="_blank"
@ -60,11 +57,9 @@ const CopyField = (props) => {
<FontAwesomeIcon icon={iconCopy} className="ml-2" />
</Button>
</div>
<UncontrolledTooltip
placement="top"
target="tooltip"
trigger="click"
>Lien copié</UncontrolledTooltip>
<UncontrolledTooltip placement="top" target="tooltip" trigger="click">
Lien copié
</UncontrolledTooltip>
</div>
);
};

@ -1,60 +1,66 @@
/**
* This component displays a bar releaving the current step
*/
import {useTranslation} from "next-i18next";
import {faArrowLeft, faCheck} from "@fortawesome/free-solid-svg-icons";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
const {Row, Col, Container} = require("reactstrap")
import { useTranslation } from 'next-i18next';
import { faArrowLeft, faCheck } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const { Row, Col, Container } = require('reactstrap');
const Step = ({name, position, active, check}) => {
const {t} = useTranslation();
const disabled = !active && !check
return <Col
className="col-auto">
<Row className={`align-items-center creation-step ${active ? 'active' : ''} ${disabled ? 'disabled' : ''}`}>
<Col className='col-auto badge align-items-center justify-content-center d-flex'>
<div>{check ? <FontAwesomeIcon icon={faCheck} /> : position}</div>
</Col>
<Col className='col-auto name'>
{t(`admin.step-${name}`)}
</Col>
</Row >
</Col >
}
const Step = ({ name, position, active, check }) => {
const { t } = useTranslation();
const disabled = !active && !check;
return (
<Col className="col-auto">
<Row
className={`align-items-center creation-step ${
active ? 'active' : ''
} ${disabled ? 'disabled' : ''}`}
>
<Col className="col-auto badge align-items-center justify-content-center d-flex">
<div>{check ? <FontAwesomeIcon icon={faCheck} /> : position}</div>
</Col>
<Col className="col-auto name">{t(`admin.step-${name}`)}</Col>
</Row>
</Col>
);
};
export const creationSteps = ['candidate', 'params', 'confirm'];
export const ProgressSteps = ({step, className, ...props}) => {
const {t} = useTranslation();
export const ProgressSteps = ({ step, className, ...props }) => {
const { t } = useTranslation();
if (!creationSteps.includes(step)) {
throw Error(`Unknown step {step}`);
}
const stepId = creationSteps.indexOf(step);
return <Row className={`w-100 m-5 d-flex ${className}`} {...props}>
<Col className='col-lg-3 col-6 mb-3'>
{step === 'candidate' ? null : (
<Row className='gx-2 align-items-end'>
<Col className='col-auto'>
<FontAwesomeIcon icon={faArrowLeft} />
</Col>
<Col className='col-auto'>
{t('admin.candidates-back-step')}
</Col>
</Row>
)
}
</Col>
<Col className='col-lg-6 col-12'>
<Row className='w-100 gx-5 justify-content-center'>
{creationSteps.map((name, i) => <Step name={name} active={step === name} check={i < stepId} key={i} position={i + 1} />
return (
<Row className={`w-100 m-5 d-flex ${className}`} {...props}>
<Col className="col-lg-3 col-6 mb-3">
{step === 'candidate' ? null : (
<Row className="gx-2 align-items-end">
<Col className="col-auto">
<FontAwesomeIcon icon={faArrowLeft} />
</Col>
<Col className="col-auto">{t('admin.candidates-back-step')}</Col>
</Row>
)}
</Row >
</Col>
<Col className='col-3'>
</Col>
</Row >
}
</Col>
<Col className="col-lg-6 col-12">
<Row className="w-100 gx-5 justify-content-center">
{creationSteps.map((name, i) => (
<Step
name={name}
active={step === name}
check={i < stepId}
key={i}
position={i + 1}
/>
))}
</Row>
</Col>
<Col className="col-3"></Col>
</Row>
);
};

@ -1,38 +1,38 @@
import * as d3 from 'd3';
const url = "https://udemy-react-d3.firebaseio.com/tallest_men.json";
const url = 'https://udemy-react-d3.firebaseio.com/tallest_men.json';
const WIDTH = 800;
const HEIGHT = 500;
export default class D3Chart {
constructor(element) {
const svg = d3.select(element)
.append("svg")
.attr("width", 800)
.attr("height", 500)
constructor(element) {
const svg = d3
.select(element)
.append('svg')
.attr('width', 800)
.attr('height', 500);
d3.json(url).then(data => {
const max = d3.max(data, d => d.height)
const y = d3.scaleLinear()
.domain([0, max])
.range([0, HEIGHT])
d3.json(url).then((data) => {
const max = d3.max(data, (d) => d.height);
const y = d3.scaleLinear().domain([0, max]).range([0, HEIGHT]);
const x = d3.scaleBand()
.domain(data.map(d => d.name))
.range([0, WIDTH])
.padding(0.4)
const x = d3
.scaleBand()
.domain(data.map((d) => d.name))
.range([0, WIDTH])
.padding(0.4);
const rects = svg.selectAll("rect")
.data(data)
const rects = svg.selectAll('rect').data(data);
rects.enter()
.append("rect")
.attr("x", d => x(d.name))
.attr("y", d => HEIGHT - y(d.height))
.attr("width", x.bandwidth)
.attr("height", d => y(d.height))
.attr("fill", "grey")
})
}
}
rects
.enter()
.append('rect')
.attr('x', (d) => x(d.name))
.attr('y', (d) => HEIGHT - y(d.height))
.attr('width', x.bandwidth)
.attr('height', (d) => y(d.height))
.attr('fill', 'grey');
});
}
}

@ -1,11 +1,12 @@
import {useState, forwardRef, ReactNode} from 'react'
import {Button, Row, Col} from 'reactstrap'
import {useTranslation} from "next-i18next";
import {faCalendarDays, faChevronDown} from "@fortawesome/free-solid-svg-icons";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import DatePicker from 'react-datepicker'
import { useState, forwardRef, ReactNode } from 'react';
import { Button, Row, Col } from 'reactstrap';
import { useTranslation } from 'next-i18next';
import {
faCalendarDays,
faChevronDown,
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import DatePicker from 'react-datepicker';
interface InputProps {
children?: ReactNode;
@ -14,36 +15,39 @@ interface InputProps {
}
export type ButtonRef = HTMLButtonElement;
const CustomDatePicker = ({date, setDate}) => {
const {t} = useTranslation();
const ExampleCustomInput = forwardRef<ButtonRef, InputProps>(({value, onClick}, ref) => (
<button onClick={onClick} ref={ref}>
<Row className='p-2 align-items-end'>
<Col className='col-auto me-auto'>
<Row className='gx-3 align-items-end'>
<Col className='col-auto'>
<FontAwesomeIcon icon={faCalendarDays} />
</Col>
<Col className='col-auto'>
{t('admin.until')}{' '}{new Date(value).toDateString()}
</Col>
</Row>
</Col>
<Col className='col-auto'>
<FontAwesomeIcon className='text-muted' icon={faChevronDown} />
</Col>
</Row>
</button>
));
return (<DatePicker
selected={date}
customInput={<ExampleCustomInput value={null} onClick={null} />}
onChange={(date) => setDate(date)}
/>);
// {/*<Button className="example-custom-input"
const CustomDatePicker = ({ date, setDate }) => {
const { t } = useTranslation();
const ExampleCustomInput = forwardRef<ButtonRef, InputProps>(
({ value, onClick }, ref) => (
<button onClick={onClick} ref={ref}>
<Row className="p-2 align-items-end">
<Col className="col-auto me-auto">
<Row className="gx-3 align-items-end">
<Col className="col-auto">
<FontAwesomeIcon icon={faCalendarDays} />
</Col>
<Col className="col-auto">
{t('admin.until')} {new Date(value).toDateString()}
</Col>
</Row>
</Col>
<Col className="col-auto">
<FontAwesomeIcon className="text-muted" icon={faChevronDown} />
</Col>
</Row>
</button>
)
);
return (
<DatePicker
selected={date}
customInput={<ExampleCustomInput value={null} onClick={null} />}
onChange={(date) => setDate(date)}
/>
);
// {/*<Button className="example-custom-input"
// {value}
// </button>*/}
// return (

@ -1,10 +1,10 @@
import Link from "next/link";
import {Container, Row, Col} from "reactstrap";
import {useTranslation} from "next-i18next";
import {CONTACT_MAIL} from '@services/constants';
import Link from 'next/link';
import { Container, Row, Col } from 'reactstrap';
import { useTranslation } from 'next-i18next';
import { CONTACT_MAIL } from '@services/constants';
const Error = ({msg}) => {
const {t} = useTranslation();
const Error = ({ msg }) => {
const { t } = useTranslation();
return (
<Container className="full-height-container">
<Row>
@ -22,7 +22,9 @@ const Error = ({msg}) => {
<Row className="mt-4">
<Col className="my-3" sm="6">
<Link href="/">
<a className="btn btn-secondary m-auto">{t("common.back-homepage")}</a>
<a className="btn btn-secondary m-auto">
{t('common.back-homepage')}
</a>
</Link>
</Col>
<Col className="my-3" sm="6">
@ -30,11 +32,11 @@ const Error = ({msg}) => {
href={`mailto:${CONTACT_MAIL}?subject=[HELP]`}
className="btn btn-success m-auto"
>
{t("error.help")}
{t('error.help')}
</a>
</Col>
</Row>
</Container >
</Container>
);
};

@ -1,9 +1,9 @@
import React from "react"
import styled from "styled-components"
import React from 'react';
import styled from 'styled-components';
const Screen = styled.div`
position: relative;
opacity: 0;
animation: fade 0.4s ease-in forwards;
background: black;
@ -23,7 +23,6 @@ const Screen = styled.div`
const Balls = styled.div`
display: flex;
.ball {
height: 20px;
@ -57,13 +56,10 @@ const Balls = styled.div`
}
`;
const LoadingScreen = () => {
return (
<Screen>
<Balls>
<div className="ball one"></div>
<div className="ball two"></div>
<div className="ball three"></div>

@ -1,29 +1,21 @@
import PropTypes from 'prop-types';
import Image from 'next/image'
import logoWithText from '../public/logos/logo.svg'
import logo from '../public/logos/logo-footer.svg'
import {useTranslation} from "next-i18next";
import Image from 'next/image';
import logoWithText from '../public/logos/logo.svg';
import logo from '../public/logos/logo-footer.svg';
import { useTranslation } from 'next-i18next';
const Logo = ({title, ...props}) => {
const {t} = useTranslation();
const Logo = ({ title, ...props }) => {
const { t } = useTranslation();
const src = title ? logoWithText : logo;
return (
<Image
src={src}
alt={t('logo.alt')}
className="d-block"
{...props}
/>
)
return <Image src={src} alt={t('logo.alt')} className="d-block" {...props} />;
};
Logo.propTypes = {
title: PropTypes.bool
title: PropTypes.bool,
};
Logo.defaultProps = {
title: true
title: true,
};
export default Logo;

@ -1,33 +1,27 @@
// TODO use bootstrap modal
// https://getbootstrap.com/docs/5.0/components/modal/
//
const Modal = ({show, onClose, children, title}) => {
const Modal = ({ show, onClose, children, title }) => {
const handleCloseClick = (e) => {
e.preventDefault();
onClose();
};
const modalContent = show ? (
<div className='vh-100 modal overlay'>
<div className='modal body'>
<div className='modal header'>
<div className="vh-100 modal overlay">
<div className="modal body">
<div className="modal header">
<a href="#" onClick={handleCloseClick}>
x
</a>
</div>
{title && <div>{title}</div>}
<div className='pt-5'>{children}</div>
<div className="pt-5">{children}</div>
</div>
</div>
) : null;
return (
modalContent
);
return modalContent;
};
export default Modal;

@ -1,9 +1,15 @@
const Switch = ({toggle, state}) => {
return (<div className="form-check form-switch">
<input onChange={toggle} className="form-check-input" type="checkbox" role="switch" checked={state} />
</div>)
}
const Switch = ({ toggle, state }) => {
return (
<div className="form-check form-switch">
<input
onChange={toggle}
className="form-check-input"
type="checkbox"
role="switch"
checked={state}
/>
</div>
);
};
export default Switch;

@ -1,11 +1,10 @@
import React, {Fragment} from 'react';
import React, { Fragment } from 'react';
import Head from 'next/head';
import dynamic from 'next/dynamic';
const Bulles = dynamic(import('./Bulles'), {
ssr: false
})
ssr: false,
});
const nbVotesPassables = 15;
const nbVotesAssezBien = 200;
@ -13,25 +12,29 @@ const nbVotesBien = 389;
const nbVotesTresBien = 12;
const nbVotesExcellent = 2;
const resultats = [nbVotesPassables, nbVotesAssezBien, nbVotesBien, nbVotesTresBien, nbVotesExcellent];
const resultats = [
nbVotesPassables,
nbVotesAssezBien,
nbVotesBien,
nbVotesTresBien,
nbVotesExcellent,
];
var totalVotes = 0;
for(var i = 0; i < resultats.length; i++) {
for (var i = 0; i < resultats.length; i++) {
totalVotes += resultats[i];
}
function SystemeVote() {
return (
<Fragment>
<Bulles donnees={resultats} />
<p style={{color: '#000000'}}>Le total des votes est de {totalVotes}.</p>
</Fragment>
);
function SystemeVote() {
return (
<Fragment>
<Bulles donnees={resultats} />
<p style={{ color: '#000000' }}>
Le total des votes est de {totalVotes}.
</p>
</Fragment>
);
}
export default SystemeVote;
export default SystemeVote;

@ -2,24 +2,22 @@
* A toggle button using bootstrap
*/
const Toggle = ({active, children}) => {
return (<button
type="button"
className={`btn btn-toggle ${active ? 'active' : ''}`}
data-toggle="button"
aria-pressed="false"
autocomplete="off"
>
{children}
</button>
)
}
const Toggle = ({ active, children }) => {
return (
<button
type="button"
className={`btn btn-toggle ${active ? 'active' : ''}`}
data-toggle="button"
aria-pressed="false"
autocomplete="off"
>
{children}
</button>
);
};
Toggle.defaultProps = {
'active': false
}
active: false,
};
export default Toggle;

@ -1,33 +1,35 @@
import {useTranslation} from "next-i18next";
import {useElection, useElectionDispatch} from './ElectionContext';
import {Container, Row, Col} from 'reactstrap'
import Switch from '@components/Switch'
import { useTranslation } from 'next-i18next';
import { useElection, useElectionDispatch } from './ElectionContext';
import { Container, Row, Col } from 'reactstrap';
import Switch from '@components/Switch';
const AccessResults = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const election = useElection();
const dispatch = useElectionDispatch();
const toggle = () => {
dispatch({
'type': 'set',
'field': 'restrictResult',
'value': !election.restrictResult
})
}
type: 'set',
field: 'restrictResult',
value: !election.restrictResult,
});
};
return (<Container className='bg-white container-fluid p-4'>
<Row>
<Col className='col-auto me-auto'>
<h4 className='text-dark'>{t('admin.access-results')}</h4>
<p className='text-muted'>{t('admin.access-results-desc')}</p>
</Col>
<Col className='col-auto d-flex align-items-center'>
<Switch toggle={toggle} state={election.restrictResult} />
</Col>
</Row>
</Container>)
}
return (
<Container className="bg-white container-fluid p-4">
<Row>
<Col className="col-auto me-auto">
<h4 className="text-dark">{t('admin.access-results')}</h4>
<p className="text-muted">{t('admin.access-results-desc')}</p>
</Col>
<Col className="col-auto d-flex align-items-center">
<Switch toggle={toggle} state={election.restrictResult} />
</Col>
</Row>
</Container>
);
};
export default AccessResults
export default AccessResults;

@ -1,6 +1,4 @@
import {useState} from "react";
import { useState } from 'react';
export default function AddPicture(props) {
const [image, setImage] = useState(null);
@ -23,15 +21,28 @@ export default function AddPicture(props) {
</div>
</div>
<div className="avatar-text">
<h4>Photo <span> (facultatif)</span></h4>
<p>Importer une photo.<br />format : jpg, png, pdf</p>
<h4>
Photo <span> (facultatif)</span>
</h4>
<p>
Importer une photo.
<br />
format : jpg, png, pdf
</p>
<div className="btn-ajout-avatar">
<input type="file" name="myImage" id="myImage" onChange={uploadToClient} />
<label className="inputfile" htmlFor="myImage">Importer une photo</label>ddpi
<input
type="file"
name="myImage"
id="myImage"
onChange={uploadToClient}
/>
<label className="inputfile" htmlFor="myImage">
Importer une photo
</label>
ddpi
</div>
</div>
</div>
);
}

@ -1,59 +1,56 @@
import {useState} from "react";
import {
faTrashAlt,
} from "@fortawesome/free-solid-svg-icons";
import {Button, Modal, ModalHeader, ModalBody, ModalFooter} from "reactstrap";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {useTranslation} from "next-i18next";
import { useState } from 'react';
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useTranslation } from 'next-i18next';
const ButtonWithConfirm = ({className, label, onDelete}) => {
const ButtonWithConfirm = ({ className, label, onDelete }) => {
const [visibled, setVisibility] = useState(false);
const {t} = useTranslation();
const { t } = useTranslation();
const toggle = () => setVisibility(!visibled)
const toggle = () => setVisibility(!visibled);
return (
<div className="input-group-append cancelButton">
<button
type="button"
className={"btn " + className}
onClick={toggle}
>
<div className="annuler"><img className="ml-0" src="/arrow-dark-left.svg" /><p className="ml-0" >Annuler</p></div>
<button type="button" className={'btn ' + className} onClick={toggle}>
<div className="annuler">
<img className="ml-0" src="/arrow-dark-left.svg" />
<p className="ml-0">Annuler</p>
</div>
</button>
<Modal
isOpen={visibled}
toggle={toggle}
className="modal-dialog-centered cancelForm"
>
<ModalHeader><FontAwesomeIcon icon={faTrashAlt} /></ModalHeader>
<ModalHeader>
<FontAwesomeIcon icon={faTrashAlt} />
</ModalHeader>
<ModalBody>
{t("Are you sure to delete")}{<br />}
{label && label !== "" ? (
<b>{label}</b>
) : (
<>{t("the row")}</>
)}
{t('Are you sure to delete')}
{<br />}
{label && label !== '' ? <b>{label}</b> : <>{t('the row')}</>}
</ModalBody>
<ModalFooter>
<Button
className={className}
onClick={toggle}>
<div className="annuler"><img src="/arrow-dark-left.svg" /> {t("No")}</div>
<Button className={className} onClick={toggle}>
<div className="annuler">
<img src="/arrow-dark-left.svg" /> {t('No')}
</div>
</Button>
<Button
className="new-btn-confirm"
onClick={() => {toggle(); onDelete();}}
className="new-btn-confirm"
onClick={() => {
toggle();
onDelete();
}}
>
<FontAwesomeIcon icon={faTrashAlt} className="mr-2"/>
{t("Yes")}
<FontAwesomeIcon icon={faTrashAlt} className="mr-2" />
{t('Yes')}
</Button>
</ModalFooter>
</Modal>
</div >
</div>
);
}
};
export default ButtonWithConfirm;

@ -1,72 +1,83 @@
/**
* This is the candidate field used during election creation
*/
import {useState} from 'react'
import Image from 'next/image'
import {useTranslation} from "next-i18next";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {
faPlus,
faTrashCan,
} from "@fortawesome/free-solid-svg-icons";
import {Row, Col} from "reactstrap";
import {useElection, useElectionDispatch} from './ElectionContext';
import defaultAvatar from '../../public/avatar.svg'
import { useState } from 'react';
import Image from 'next/image';
import { useTranslation } from 'next-i18next';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlus, faTrashCan } from '@fortawesome/free-solid-svg-icons';
import { Row, Col } from 'reactstrap';
import { useElection, useElectionDispatch } from './ElectionContext';
import defaultAvatar from '../../public/avatar.svg';
import CandidateModalSet from './CandidateModalSet';
import CandidateModalDel from './CandidateModalDel';
const CandidateField = ({position, className = '', ...inputProps}) => {
const {t} = useTranslation();
const CandidateField = ({ position, className = '', ...inputProps }) => {
const { t } = useTranslation();
const election = useElection();
const dispatch = useElectionDispatch();
const candidate = election.candidates[position];
const image = candidate && candidate.image ? candidate.image : defaultAvatar;
const active = candidate && candidate.active === true
const active = candidate && candidate.active === true;
const [modalDel, setModalDel] = useState(false);
const [modalSet, setModalSet] = useState(false);
const addCandidate = () => {
dispatch({'type': 'candidate-push', 'value': "default"})
dispatch({ type: 'candidate-push', value: 'default' });
};
const toggleSet = () => setModalSet(m => !m)
const toggleDel = () => setModalDel(m => !m)
const toggleSet = () => setModalSet((m) => !m);
const toggleDel = () => setModalDel((m) => !m);
return (
<Row
className={`${className || ""} p-2 my-3 border border-dashed border-2 border-light border-opacity-25 align-items-center ${active ? "active" : ""}`}
className={`${
className || ''
} p-2 my-3 border border-dashed border-2 border-light border-opacity-25 align-items-center ${
active ? 'active' : ''
}`}
{...inputProps}
>
<Col onClick={toggleSet} className='cursor-pointer col-auto me-auto'>
<Row className='gx-3'>
<Col className='col-auto'>
<Image src={image} width={24} height={24} className={image == defaultAvatar ? "default-avatar" : ""} alt={t('common.thumbnail')} />
<Col onClick={toggleSet} className="cursor-pointer col-auto me-auto">
<Row className="gx-3">
<Col className="col-auto">
<Image
src={image}
width={24}
height={24}
className={image == defaultAvatar ? 'default-avatar' : ''}
alt={t('common.thumbnail')}
/>
</Col>
<Col className='col-auto fw-bold'>
{candidate.name ? candidate.name : t("admin.add-candidate")}
<Col className="col-auto fw-bold">
{candidate.name ? candidate.name : t('admin.add-candidate')}
</Col>
</Row>
</Col>
<Col className='col-auto cursor-pointer'>
{active ?
<Col className="col-auto cursor-pointer">
{active ? (
<FontAwesomeIcon
icon={faTrashCan}
onClick={() => setModalDel(m => !m)}
/> :
<FontAwesomeIcon
icon={faPlus}
onClick={addCandidate}
onClick={() => setModalDel((m) => !m)}
/>
}
) : (
<FontAwesomeIcon icon={faPlus} onClick={addCandidate} />
)}
</Col>
<CandidateModalSet toggle={toggleSet} isOpen={modalSet} position={position} />
<CandidateModalDel toggle={toggleDel} isOpen={modalDel} position={position} />
</Row >
<CandidateModalSet
toggle={toggleSet}
isOpen={modalSet}
position={position}
/>
<CandidateModalDel
toggle={toggleDel}
isOpen={modalDel}
position={position}
/>
</Row>
);
}
};
export default CandidateField;

@ -1,72 +1,73 @@
import {
Row,
Col,
Label,
Input,
Modal,
ModalBody,
Form
} from "reactstrap";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import { Row, Col, Label, Input, Modal, ModalBody, Form } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faTrashCan,
faTrashAlt,
faArrowLeft,
} from "@fortawesome/free-solid-svg-icons";
import {useTranslation} from "next-i18next";
} from '@fortawesome/free-solid-svg-icons';
import { useTranslation } from 'next-i18next';
import Image from 'next/image';
import {useElection, useElectionDispatch} from './ElectionContext';
import Button from '@components/Button'
import {upload} from '@services/imgpush';
import {IMGPUSH_URL} from '@services/constants';
import defaultAvatar from '../../public/default-avatar.svg'
import {useEffect} from "react";
import { useElection, useElectionDispatch } from './ElectionContext';
import Button from '@components/Button';
import { upload } from '@services/imgpush';
import { IMGPUSH_URL } from '@services/constants';
import defaultAvatar from '../../public/default-avatar.svg';
import { useEffect } from 'react';
const CandidateModal = ({isOpen, position, toggle}) => {
const {t} = useTranslation();
const CandidateModal = ({ isOpen, position, toggle }) => {
const { t } = useTranslation();
const election = useElection();
const dispatch = useElectionDispatch();
const candidate = election.candidates[position];
const removeCandidate = () => {
dispatch({'type': 'candidate-rm', 'position': position})
}
dispatch({ type: 'candidate-rm', position: position });
};
return (
<Modal
isOpen={isOpen}
toggle={toggle}
keyboard={true}
className='modal_candidate'
className="modal_candidate"
>
<ModalBody className='flex-column justify-contenter-center d-flex p-4'>
<Row className='justify-content-center'>
<Col className='col-auto px-4 py-4 rounded-circle bg-light'>
<ModalBody className="flex-column justify-contenter-center d-flex p-4">
<Row className="justify-content-center">
<Col className="col-auto px-4 py-4 rounded-circle bg-light">
<FontAwesomeIcon size="2x" icon={faTrashCan} />
</Col>
</Row>
<p className='text-danger fw-bold text-center mt-4'>{t('admin.candidate-confirm-del')}
<p className="text-danger fw-bold text-center mt-4">
{t('admin.candidate-confirm-del')}
</p>
{candidate.name ? <h4 className='text-center'>{candidate.name}</h4> : null}
<Row className='mt-5 mb-3'>
<Col className='col-auto me-auto'>
<Button onClick={toggle} color='dark' icon={faArrowLeft} outline={true}>
{candidate.name ? (
<h4 className="text-center">{candidate.name}</h4>
) : null}
<Row className="mt-5 mb-3">
<Col className="col-auto me-auto">
<Button
onClick={toggle}
color="dark"
icon={faArrowLeft}
outline={true}
>
{t('admin.candidate-confirm-back')}
</Button>
</Col>
<Col className='col-auto '>
<Button icon={faTrashAlt} outline={true} color="primary" onClick={removeCandidate}>
<Col className="col-auto ">
<Button
icon={faTrashAlt}
outline={true}
color="primary"
onClick={removeCandidate}
>
{t('admin.candidate-confirm-ok')}
</Button>
</Col>
</Row >
</ModalBody >
</Modal >);
}
</Row>
</ModalBody>
</Modal>
);
};
export default CandidateModal;

@ -1,128 +1,124 @@
import {useState, useEffect, useRef} from 'react'
import {
Row,
Col,
Label,
Input,
Modal,
ModalBody,
Form
} from "reactstrap";
import {
faPlus,
faArrowLeft,
} from "@fortawesome/free-solid-svg-icons";
import {useTranslation} from "next-i18next";
import { useState, useEffect, useRef } from 'react';
import { Row, Col, Label, Input, Modal, ModalBody, Form } from 'reactstrap';
import { faPlus, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { useTranslation } from 'next-i18next';
import Image from 'next/image';
import {useElection, useElectionDispatch} from './ElectionContext';
import Button from '@components/Button'
import {upload} from '@services/imgpush';
import {IMGPUSH_URL} from '@services/constants';
import defaultAvatar from '../../public/default-avatar.svg'
import { useElection, useElectionDispatch } from './ElectionContext';
import Button from '@components/Button';
import { upload } from '@services/imgpush';
import { IMGPUSH_URL } from '@services/constants';
import defaultAvatar from '../../public/default-avatar.svg';
const CandidateModal = ({isOpen, position, toggle}) => {
const {t} = useTranslation();
const CandidateModal = ({ isOpen, position, toggle }) => {
const { t } = useTranslation();
const election = useElection();
const dispatch = useElectionDispatch();
const candidate = election.candidates[position];
const [state, setState] = useState(candidate);
const image = state.image && state.image != "" ? state.image : defaultAvatar;
const image = state.image && state.image != '' ? state.image : defaultAvatar;
const handleFile = async (event) => {
const payload = await upload(event.target.files[0])
setState(s => ({...s, "image": `${IMGPUSH_URL}/${payload['filename']}`}))
}
const payload = await upload(event.target.files[0]);
setState((s) => ({ ...s, image: `${IMGPUSH_URL}/${payload['filename']}` }));
};
// to manage the hidden ugly file input
const hiddenFileInput = useRef(null);
useEffect(() => {
setState(election.candidates[position]);
console.log('effect election', election)
}, [election])
console.log('effect election', election);
}, [election]);
const save = () => {
dispatch({
'type': 'candidate-set',
'position': position,
'field': "image",
'value': state.image,
})
type: 'candidate-set',
position: position,
field: 'image',
value: state.image,
});
dispatch({
'type': 'candidate-set',
'position': position,
'field': "name",
'value': state.name,
})
type: 'candidate-set',
position: position,
field: 'name',
value: state.name,
});
dispatch({
'type': 'candidate-set',
'position': position,
'field': "description",
'value': state.description,
})
type: 'candidate-set',
position: position,
field: 'description',
value: state.description,
});
toggle();
}
};
const handleName = (e) => {
setState(s => ({...s, 'name': e.target.value}))
}
setState((s) => ({ ...s, name: e.target.value }));
};
const handleDescription = (e) => {
setState(s => ({...s, 'description': e.target.value}))
}
setState((s) => ({ ...s, description: e.target.value }));
};
return (
<Modal
isOpen={isOpen}
toggle={toggle}
keyboard={true}
className='modal_candidate'
className="modal_candidate"
>
<div className="modal-header p-4">
<h4 className="modal-title">
{t('admin.add-candidate')}
</h4>
<button type="button" onClick={toggle} className="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
<h4 className="modal-title">{t('admin.add-candidate')}</h4>
<button
type="button"
onClick={toggle}
className="btn-close"
data-bs-dismiss="modal"
aria-label="Close"
></button>
</div>
<ModalBody className='p-4'>
<p>{t('admin.add-candidate-desc')}
</p>
<ModalBody className="p-4">
<p>{t('admin.add-candidate-desc')}</p>
<Col>
<Form className='container container-fluid'>
<Row className='gx-4 mb-3'>
<Col className='col-auto'>
<Image src={image} alt={t('admin.photo')} height={120} width={120} />
<Form className="container container-fluid">
<Row className="gx-4 mb-3">
<Col className="col-auto">
<Image
src={image}
alt={t('admin.photo')}
height={120}
width={120}
/>
</Col>
<Col className='col-auto'>
<Label className='fw-bold'>{t('admin.photo')} <span className='text-muted'> ({t('admin.optional')})</span></Label>
<Col className="col-auto">
<Label className="fw-bold">
{t('admin.photo')}{' '}
<span className="text-muted"> ({t('admin.optional')})</span>
</Label>
<p>{t('admin.photo-type')} jpg, png, pdf</p>
<div>
<input
type="file"
className='hide'
className="hide"
onChange={handleFile}
ref={hiddenFileInput}
/>
<Button
color='dark'
color="dark"
outline={true}
onClick={() => hiddenFileInput.current.click()}
>
{t('admin.photo-import')}
</Button>
</div>
</Col>
</Row>
<div className='mb-3'>
<Label className='fw-bold'>{t('common.name')} </Label>
<div className="mb-3">
<Label className="fw-bold">{t('common.name')} </Label>
<Input
type="text"
placeholder={t("admin.candidate-name-placeholder")}
placeholder={t('admin.candidate-name-placeholder')}
tabIndex={position + 1}
value={state.name}
onChange={handleName}
@ -131,34 +127,46 @@ const CandidateModal = ({isOpen, position, toggle}) => {
required
/>
</div>
<div className=''>
<Label className='fw-bold'>{t('common.description')} <span className='text-muted'> ({t('admin.optional')})</span></Label>
<div className="">
<Label className="fw-bold">
{t('common.description')}{' '}
<span className="text-muted"> ({t('admin.optional')})</span>
</Label>
<Input
type="text"
defaultValue={candidate.description}
placeholder={t("admin.candidate-desc-placeholder")}
placeholder={t('admin.candidate-desc-placeholder')}
onChange={handleDescription}
value={state.description}
maxLength="250"
/>
</div>
<Row className='mt-5 mb-3'>
<Col className='col-auto me-auto'>
<Button onClick={toggle} color='dark' outline={true} icon={faArrowLeft} >
<Row className="mt-5 mb-3">
<Col className="col-auto me-auto">
<Button
onClick={toggle}
color="dark"
outline={true}
icon={faArrowLeft}
>
{t('common.cancel')}
</Button>
</Col>
<Col className='col-auto '>
<Button outline={true} color="primary" onClick={save} icon={faPlus}>
<Col className="col-auto ">
<Button
outline={true}
color="primary"
onClick={save}
icon={faPlus}
>
{t('common.save')}
</Button>
</Col>
</Row>
</Form>
</Col>
</ModalBody >
</Modal >);
}
</ModalBody>
</Modal>
);
};
export default CandidateModal;

@ -1,57 +1,56 @@
import {useState, useEffect, createRef} from 'react'
import {useTranslation} from "next-i18next";
import {Container} from 'reactstrap';
import {faArrowRight} from "@fortawesome/free-solid-svg-icons";
import {MAX_NUM_CANDIDATES} from '@services/constants';
import Alert from '@components/Alert'
import Button from '@components/Button'
import {useElection, useElectionDispatch} from './ElectionContext';
import CandidateField from './CandidateField'
const CandidatesField = ({onSubmit}) => {
const {t} = useTranslation();
import { useState, useEffect, createRef } from 'react';
import { useTranslation } from 'next-i18next';
import { Container } from 'reactstrap';
import { faArrowRight } from '@fortawesome/free-solid-svg-icons';
import { MAX_NUM_CANDIDATES } from '@services/constants';
import Alert from '@components/Alert';
import Button from '@components/Button';
import { useElection, useElectionDispatch } from './ElectionContext';
import CandidateField from './CandidateField';
const CandidatesField = ({ onSubmit }) => {
const { t } = useTranslation();
const election = useElection();
const dispatch = useElectionDispatch();
const candidates = election.candidates;
const [error, setError] = useState(null)
const disabled = candidates.filter(c => c.name !== "").length < 2;
const [error, setError] = useState(null);
const disabled = candidates.filter((c) => c.name !== '').length < 2;
// What to do when we change the candidates
useEffect(() => {
// Initialize the list with at least two candidates
if (candidates.length < 2) {
dispatch({'type': 'candidate-push', 'value': "default"})
dispatch({ type: 'candidate-push', value: 'default' });
}
if (candidates.length > MAX_NUM_CANDIDATES) {
setError('error.too-many-candidates')
setError('error.too-many-candidates');
}
}, [candidates])
}, [candidates]);
return (
<Container className="candidate flex-grow-1 mt-5 flex-column d-flex justify-content-between">
<div className="d-flex flex-column">
<h4 className='mb-4'>{t('admin.add-candidates')}</h4>
<h4 className="mb-4">{t('admin.add-candidates')}</h4>
<Alert msg={error} />
{candidates.map((candidate, index) => {
return (
<CandidateField
key={index}
position={index}
/>
)
return <CandidateField key={index} position={index} />;
})}
</div>
<div className="mb-5 d-flex justify-content-center">
<Button outline={true} color="secondary" onClick={onSubmit} disabled={disabled} icon={faArrowRight} position="right">
<Button
outline={true}
color="secondary"
onClick={onSubmit}
disabled={disabled}
icon={faArrowRight}
position="right"
>
{t('admin.candidates-submit')}
</Button>
</div>
</Container >
</Container>
);
}
export default CandidatesField
};
export default CandidatesField;

@ -1,71 +1,78 @@
import {useTranslation} from "next-i18next";
import Footer from "@components/layouts/Footer";
import TrashButton from "./TrashButton";
import { useTranslation } from 'next-i18next';
import Footer from '@components/layouts/Footer';
import TrashButton from './TrashButton';
import {
faExclamationTriangle,
faCheck,
faArrowLeft,
faPen,
} from "@fortawesome/free-solid-svg-icons";
import {Button, Modal, ModalHeader, ModalBody, ModalFooter, Row, Col, Label, Container} from "reactstrap";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {useElection} from "./ElectionContext";
import CandidateField from "./CandidateField";
} from '@fortawesome/free-solid-svg-icons';
import {
Button,
Modal,
ModalHeader,
ModalBody,
ModalFooter,
Row,
Col,
Label,
Container,
} from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useElection } from './ElectionContext';
import CandidateField from './CandidateField';
const TitleField = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const election = useElection();
return <Container className='bg-white container-fluid p-4'>
<Row>
<Col className='col-auto me-auto'>
<h4 className='text-dark'>{t('admin.access-results')}</h4>
</Col>
</Row>
<h4 className='text-primary'>
{election.title}
</h4>
</Container>
}
return (
<Container className="bg-white container-fluid p-4">
<Row>
<Col className="col-auto me-auto">
<h4 className="text-dark">{t('admin.access-results')}</h4>
</Col>
</Row>
<h4 className="text-primary">{election.title}</h4>
</Container>
);
};
const CandidatesField = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const election = useElection();
return <Container className='bg-white container-fluid p-4'>
<Row>
<Col className='col-auto me-auto'>
<h4 className='text-dark'>{t('admin.access-results')}</h4>
</Col>
<Col className='col-auto d-flex align-items-center'>
<FontAwesomeIcon icon={faPen} />
</Col>
</Row>
{election.candidates.map((_, i) =>
<CandidateField position={i} />
)}
</Container>
}
return (
<Container className="bg-white container-fluid p-4">
<Row>
<Col className="col-auto me-auto">
<h4 className="text-dark">{t('admin.access-results')}</h4>
</Col>
<Col className="col-auto d-flex align-items-center">
<FontAwesomeIcon icon={faPen} />
</Col>
</Row>
{election.candidates.map((_, i) => (
<CandidateField position={i} />
))}
</Container>
);
};
const ConfirmField = ({onSubmit, goToCandidates, goToParams}) => {
const {t} = useTranslation();
const ConfirmField = ({ onSubmit, goToCandidates, goToParams }) => {
const { t } = useTranslation();
const election = useElection();
return (
<Container className="params flex-grow-1 my-5 mt-5 flex-column d-flex justify-content-between">
<Row>
<Col className='col-md-auto col-12'>
<h4 className='mb-3'>{t('common.the-vote')}</h4>
<Col className="col-md-auto col-12">
<h4 className="mb-3">{t('common.the-vote')}</h4>
<TitleField />
<CandidatesField />
</Col>
<Col className='col-md-auto col-12'>
</Col>
<Col className="col-md-auto col-12"></Col>
</Row>
</Container>
)
}
);
};
export default ConfirmField
export default ConfirmField;

@ -1,30 +1,26 @@
/**
* This component manages the date for ending the election
*/
import {useState} from 'react';
import {Row} from 'reactstrap';
import {useTranslation} from "next-i18next";
import {useElection, useElectionDispatch} from './ElectionContext';
import { useState } from 'react';
import { Row } from 'reactstrap';
import { useTranslation } from 'next-i18next';
import { useElection, useElectionDispatch } from './ElectionContext';
import Toggle from '@components/Toggle';
const DateField = () => {
const election = useElection();
const dispatch = useElectionDispatch();
const [toggle, setToggle] = useState(false)
const {t} = useTranslation();
const [toggle, setToggle] = useState(false);
const { t } = useTranslation();
return (<Row>
<Col className='col-auto me-auto'>
{t('admin.date-limit')}
</Col>
<Col className='col-auto'>
<Toggle onChange={setToggle(t => !t)} />
</Col>
</Row>)
}
return (
<Row>
<Col className="col-auto me-auto">{t('admin.date-limit')}</Col>
<Col className="col-auto">
<Toggle onChange={setToggle((t) => !t)} />
</Col>
</Row>
);
};
export default DateField;

@ -1,25 +1,20 @@
/**
* This file provides a context and a reducer to manage an election
*/
import {createContext, useContext, useReducer, useEffect} from 'react';
import {useRouter} from "next/router";
import {DEFAULT_GRADES} from '@services/constants';
import { createContext, useContext, useReducer, useEffect } from 'react';
import { useRouter } from 'next/router';
import { DEFAULT_GRADES } from '@services/constants';
// Store data about an election
const ElectionContext = createContext(null);
// Store the dispatch function that can modify an election
const ElectionDispatchContext = createContext(null);
export function ElectionProvider({children}) {
export function ElectionProvider({ children }) {
/**
* Provide the election and the dispatch to all children components
*/
const [election, dispatch] = useReducer(
electionReducer,
initialElection
);
const [election, dispatch] = useReducer(electionReducer, initialElection);
// At the initialization, set the title using GET param
const router = useRouter();
@ -27,18 +22,15 @@ export function ElectionProvider({children}) {
if (!router.isReady) return;
dispatch({
'type': 'set',
'field': 'title',
'value': router.query.title || ""
})
type: 'set',
field: 'title',
value: router.query.title || '',
});
}, [router.isReady]);
return (
<ElectionContext.Provider value={election}>
<ElectionDispatchContext.Provider
value={dispatch}
>
<ElectionDispatchContext.Provider value={dispatch}>
{children}
</ElectionDispatchContext.Provider>
</ElectionContext.Provider>
@ -60,66 +52,68 @@ export function useElectionDispatch() {
}
function electionReducer(election, action) {
/**
/**
* Manage all types of action doable on an election
*/
switch (action.type) {
case 'set': {
return {...election, [action.field]: action.value};
return { ...election, [action.field]: action.value };
}
case 'commit': {
throw Error('Not implemented yet')
throw Error('Not implemented yet');
}
case 'remove': {
throw Error('Not implemented yet')
throw Error('Not implemented yet');
}
case 'candidate-push': {
const candidate = action.value === 'default' ? {...defaultCandidate} : action.value;
const candidate =
action.value === 'default' ? { ...defaultCandidate } : action.value;
const candidates = [...election.candidates, candidate];
return {...election, candidates}
return { ...election, candidates };
}
case 'candidate-rm': {
if (typeof action.position !== "number") {
throw Error(`Unexpected candidate position ${action.position}`)
if (typeof action.position !== 'number') {
throw Error(`Unexpected candidate position ${action.position}`);
}
const candidates = [...election.candidates];
candidates.splice(action.position)
return {...election, candidates}
candidates.splice(action.position);
return { ...election, candidates };
}
case 'candidate-set': {
if (typeof action.position !== "number") {
throw Error(`Unexpected candidate position ${action.value}`)
if (typeof action.position !== 'number') {
throw Error(`Unexpected candidate position ${action.value}`);
}
if (action.field === "active") {
throw Error("You are not allowed the set the active flag")
if (action.field === 'active') {
throw Error('You are not allowed the set the active flag');
}
const candidates = [...election.candidates];
const candidate = candidates[action.position]
candidate[action.field] = action.value
const candidate = candidates[action.position];
candidate[action.field] = action.value;
candidate['active'] = true;
return {...election, candidates}
return { ...election, candidates };
}
case 'grade-push': {
const grade = action.value === 'default' ? {...defaultCandidate} : action.value;
const grade =
action.value === 'default' ? { ...defaultCandidate } : action.value;
const grades = [...election.grades, grade];
return {...election, grades}
return { ...election, grades };
}
case 'grade-rm': {
if (typeof action.position !== "number") {
throw Error(`Unexpected grade position ${action.position}`)
if (typeof action.position !== 'number') {
throw Error(`Unexpected grade position ${action.position}`);
}
const grades = [...election.grades];
grades.splice(action.position)
return {...election, grades}
grades.splice(action.position);
return { ...election, grades };
}
case 'grade-set': {
if (typeof action.position !== "number") {
throw Error(`Unexpected grade position ${action.value}`)
if (typeof action.position !== 'number') {
throw Error(`Unexpected grade position ${action.value}`);
}
const grades = [...election.grades];
const grade = grades[action.position]
const grade = grades[action.position];
grade[action.field] = action.value;
return {...election, grades}
return { ...election, grades };
}
default: {
throw Error(`Unknown action: ${action.type}`);
@ -128,15 +122,15 @@ function electionReducer(election, action) {
}
const defaultCandidate = {
name: "",
description: "",
name: '',
description: '',
active: false,
}
};
const initialElection = {
title: "",
description: "",
candidates: [{...defaultCandidate}, {...defaultCandidate}],
title: '',
description: '',
candidates: [{ ...defaultCandidate }, { ...defaultCandidate }],
grades: [],
isTimeLimited: false,
isRandomOrder: false,

@ -1,11 +1,13 @@
import React from "react";
import React from 'react';
export const DotButton = ({ selected, onClick, value }) => (
<button
className={`embla__dot ${selected ? "is-selected" : ""}`}
className={`embla__dot ${selected ? 'is-selected' : ''}`}
type="button"
onClick={onClick}
>{value}</button>
>
{value}
</button>
);
export const PrevButton = ({ enabled, onClick }) => (

@ -1,14 +1,18 @@
import {useState} from 'react'
import {Row, Col} from 'reactstrap';
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import { useState } from 'react';
import { Row, Col } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faPlus, faPen, faXmark, faCheck, faRotateLeft
} from "@fortawesome/free-solid-svg-icons";
import {useElection, useElectionDispatch} from './ElectionContext';
const GradeField = ({value}) => {
faPlus,
faPen,
faXmark,
faCheck,
faRotateLeft,
} from '@fortawesome/free-solid-svg-icons';
import { useElection, useElectionDispatch } from './ElectionContext';
const GradeField = ({ value }) => {
const [modal, setModal] = useState(false);
const toggle = () => setModal(m => !m)
const toggle = () => setModal((m) => !m);
const election = useElection();
const grade = election.grades[value];
@ -29,28 +33,28 @@ const GradeField = ({value}) => {
position: value,
field: 'active',
value: !grade.active,
})
}
});
};
const style = {
color: grade.active ? "white" : "#8F88BA",
backgroundColor: grade.active ? grade.color : "#F2F0FF",
}
return <Row
style={style}
onClick={toggle}
className='p-2 m-1 rounded-1'
>
<Col className={`${grade.active ? "" : "text-decoration-line-through"} col-auto fw-bold`}>
{grade.name}
</Col>
<Col onClick={handleActive} className='col-auto'>
<FontAwesomeIcon icon={grade.active ? faXmark : faRotateLeft} />
</Col>
</Row >
}
color: grade.active ? 'white' : '#8F88BA',
backgroundColor: grade.active ? grade.color : '#F2F0FF',
};
return (
<Row style={style} onClick={toggle} className="p-2 m-1 rounded-1">
<Col
className={`${
grade.active ? '' : 'text-decoration-line-through'
} col-auto fw-bold`}
>
{grade.name}
</Col>
<Col onClick={handleActive} className="col-auto">
<FontAwesomeIcon icon={grade.active ? faXmark : faRotateLeft} />
</Col>
</Row>
);
};
export default GradeField;

@ -1,70 +1,79 @@
/**
* A field to update the grades
*/
import {useState, useEffect} from 'react'
import {useTranslation} from "next-i18next";
import {Container, Row, Col} from 'reactstrap';
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import { useState, useEffect } from 'react';
import { useTranslation } from 'next-i18next';
import { Container, Row, Col } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faPlus, faPen, faXmark, faCheck
} from "@fortawesome/free-solid-svg-icons";
import {DEFAULT_GRADES, GRADE_COLORS} from '@services/constants';
import {useElection, useElectionDispatch} from './ElectionContext';
faPlus,
faPen,
faXmark,
faCheck,
} from '@fortawesome/free-solid-svg-icons';
import { DEFAULT_GRADES, GRADE_COLORS } from '@services/constants';
import { useElection, useElectionDispatch } from './ElectionContext';
import GradeField from './GradeField';
const AddField = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const [modal, setModal] = useState(false);
const toggle = () => setModal(m => !m)
const toggle = () => setModal((m) => !m);
const dispatch = useElectionDispatch();
return <Row
onClick={toggle}
className='border border-2 border-primary text-black p-2 m-1 rounded-pill'
>
<Col className='col-auto'>
<FontAwesomeIcon icon={faPlus} />
</Col>
</Row >
}
return (
<Row
onClick={toggle}
className="border border-2 border-primary text-black p-2 m-1 rounded-pill"
>
<Col className="col-auto">
<FontAwesomeIcon icon={faPlus} />
</Col>
</Row>
);
};
const Grades = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const defaultEndDate = new Date();
defaultEndDate.setUTCDate(defaultEndDate.getUTCDate() + 15)
defaultEndDate.setUTCDate(defaultEndDate.getUTCDate() + 15);
const [endDate, setEndDate] = useState(defaultEndDate);
useEffect(() => {
dispatch({
type: "set",
field: "grades",
type: 'set',
field: 'grades',
value: DEFAULT_GRADES.map((g, i) => ({
name: t(g),
active: true,
color: GRADE_COLORS[i]
}))
})
console.log('foo')
}, [])
color: GRADE_COLORS[i],
})),
});
console.log('foo');
}, []);
const election = useElection();
const grades = election.grades;
const dispatch = useElectionDispatch();
return (<Container className='bg-white container-fluid p-4 mt-1'>
<Row>
<Col className='col-auto me-auto'>
<h4 className='text-dark'>{t('common.grades')}</h4>
<p className='text-muted'>{t('admin.grades-desc')}</p>
</Col>
<Col className='col-auto d-flex align-items-center'>
{grades.map((_, i) => <GradeField value={i} key={i} />)}
{ /* <AddField /> */}
</Col>
</Row>
</Container>)
}
return (
<Container className="bg-white container-fluid p-4 mt-1">
<Row>
<Col className="col-auto me-auto">
<h4 className="text-dark">{t('common.grades')}</h4>
<p className="text-muted">{t('admin.grades-desc')}</p>
</Col>
<Col className="col-auto d-flex align-items-center">
{grades.map((_, i) => (
<GradeField value={i} key={i} />
))}
{/* <AddField /> */}
</Col>
</Row>
</Container>
);
};
export default Grades;

@ -1,26 +1,26 @@
/* eslint react/prop-types: 0 */
import React, { Component } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faQuestionCircle } from "@fortawesome/free-solid-svg-icons";
import React, { Component } from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons';
class HelpButton extends Component {
constructor(props) {
super(props);
this.state = {
tooltipOpen: false
tooltipOpen: false,
};
}
showTooltip = () => {
this.setState({
tooltipOpen: true
tooltipOpen: true,
});
};
hideTooltip = () => {
this.setState({
tooltipOpen: false
tooltipOpen: false,
});
};
@ -31,30 +31,30 @@ class HelpButton extends Component {
{this.state.tooltipOpen ? (
<span
style={{
position: "absolute",
position: 'absolute',
zIndex: 10,
fontSize: "12px",
color: "#000",
backgroundColor: "#fff",
display: "inline-block",
borderRadius: "0.25rem",
boxShadow: "-5px 0 5px rgba(0,0,0,0.5)",
maxWidth: "200px",
padding: "10px",
marginLeft: "-215px",
marginTop: "-25px"
fontSize: '12px',
color: '#000',
backgroundColor: '#fff',
display: 'inline-block',
borderRadius: '0.25rem',
boxShadow: '-5px 0 5px rgba(0,0,0,0.5)',
maxWidth: '200px',
padding: '10px',
marginLeft: '-215px',
marginTop: '-25px',
}}
>
<span
style={{
position: "absolute",
position: 'absolute',
width: 0,
height: 0,
borderTop: "10px solid transparent",
borderBottom: "10px solid transparent",
borderLeft: "10px solid #fff",
marginLeft: "190px",
marginTop: "15px"
borderTop: '10px solid transparent',
borderBottom: '10px solid transparent',
borderLeft: '10px solid #fff',
marginLeft: '190px',
marginTop: '15px',
}}
></span>
{this.props.children}

@ -1,15 +1,14 @@
import {useState} from 'react'
import {useTranslation} from "next-i18next";
import {Container, Row, Col} from 'reactstrap';
import DatePicker from '@components/DatePicker'
import Switch from '@components/Switch'
import {useElection, useElectionDispatch} from './ElectionContext';
import { useState } from 'react';
import { useTranslation } from 'next-i18next';
import { Container, Row, Col } from 'reactstrap';
import DatePicker from '@components/DatePicker';
import Switch from '@components/Switch';
import { useElection, useElectionDispatch } from './ElectionContext';
const LimitDate = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const defaultEndDate = new Date();
defaultEndDate.setUTCDate(defaultEndDate.getUTCDate() + 15)
defaultEndDate.setUTCDate(defaultEndDate.getUTCDate() + 15);
const [endDate, setEndDate] = useState(defaultEndDate);
const election = useElection();
@ -19,48 +18,50 @@ const LimitDate = () => {
const toggle = () => {
dispatch({
'type': 'set',
'field': 'endVote',
'value': hasDate ? null : endDate,
})
}
type: 'set',
field: 'endVote',
value: hasDate ? null : endDate,
});
};
const desc = t('admin.limit-duration-desc');
const now = new Date();
const oneDay = 24 * 60 * 60 * 1000;
const remainingDays = Math.ceil((endDate.getTime() - now.getTime()) / oneDay);
return (<Container className='bg-white container-fluid p-4 mt-1'>
<Row>
<Col className='col-auto me-auto'>
<h4 className='text-dark'>
{t('admin.limit-duration')}
{hasDate ? <> {' '} <div className="badge ml-3 text-bg-light text-black-50">
{`${t("admin.ending-in")} ${remainingDays} ${t("common.days")}`}
</div> </> : null}
</h4>
{desc === "" ? null :
<p className='text-muted'>{desc}</p>
}
</Col>
<Col className='col-auto d-flex align-items-center'>
<Switch toggle={toggle} state={hasDate} />
</Col>
</Row>
{
hasDate ?
return (
<Container className="bg-white container-fluid p-4 mt-1">
<Row>
<Col className="col-auto me-auto">
<h4 className="text-dark">
{t('admin.limit-duration')}
{hasDate ? (
<>
{' '}
<div className="badge ml-3 text-bg-light text-black-50">
{`${t('admin.ending-in')} ${remainingDays} ${t(
'common.days'
)}`}
</div>{' '}
</>
) : null}
</h4>
{desc === '' ? null : <p className="text-muted">{desc}</p>}
</Col>
<Col className="col-auto d-flex align-items-center">
<Switch toggle={toggle} state={hasDate} />
</Col>
</Row>
{hasDate ? (
<Row>
<Col className='col-auto'>
<Col className="col-auto">
<DatePicker date={endDate} setDate={setEndDate} />
</Col>
<Col className='col-auto'>
</Col>
<Col className="col-auto"></Col>
</Row>
: null
}
</Container >)
}
) : null}
</Container>
);
};
export default LimitDate;

@ -1,20 +1,18 @@
import {useTranslation} from "next-i18next";
import {Container} from 'reactstrap';
import {faArrowRight} from "@fortawesome/free-solid-svg-icons";
import Button from '@components/Button'
import Grades from './Grades'
import LimitDate from './LimitDate'
import AccessResults from './AccessResults'
import Private from './Private'
import { useTranslation } from 'next-i18next';
import { Container } from 'reactstrap';
import { faArrowRight } from '@fortawesome/free-solid-svg-icons';
import Button from '@components/Button';
import Grades from './Grades';
import LimitDate from './LimitDate';
import AccessResults from './AccessResults';
import Private from './Private';
const ParamsField = ({onSubmit}) => {
const {t} = useTranslation();
const ParamsField = ({ onSubmit }) => {
const { t } = useTranslation();
return (
<Container className="params flex-grow-1 my-5 mt-5 flex-column d-flex justify-content-between">
<div className="d-flex flex-column">
<AccessResults />
<LimitDate />
<Grades />
@ -31,10 +29,8 @@ const ParamsField = ({onSubmit}) => {
{t('admin.params-submit')}
</Button>
</div>
</Container >
</Container>
);
}
export default ParamsField
};
export default ParamsField;

@ -1,14 +1,14 @@
/**
* A field to update the grades
*/
import {useState} from 'react'
import {useTranslation} from "next-i18next";
import {Container, Row, Col} from 'reactstrap';
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faCircleInfo} from "@fortawesome/free-solid-svg-icons";
import Switch from '@components/Switch'
import ListInput from '@components/ListInput'
import {useElection, useElectionDispatch} from './ElectionContext';
import { useState } from 'react';
import { useTranslation } from 'next-i18next';
import { Container, Row, Col } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCircleInfo } from '@fortawesome/free-solid-svg-icons';
import Switch from '@components/Switch';
import ListInput from '@components/ListInput';
import { useElection, useElectionDispatch } from './ElectionContext';
const validateEmail = (email) => {
// https://stackoverflow.com/a/46181/4986615
@ -20,7 +20,7 @@ const validateEmail = (email) => {
};
const Private = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const [emails, setEmails] = useState([]);
@ -29,41 +29,50 @@ const Private = () => {
const toggle = () => {
dispatch({
'type': 'set',
'field': 'restrictVote',
'value': !election.restrictVote,
})
}
type: 'set',
field: 'restrictVote',
value: !election.restrictVote,
});
};
const handleEmails = (emails) => {
dispatch({
'type': 'set',
'field': 'emails',
'value': emails,
})
}
type: 'set',
field: 'emails',
value: emails,
});
};
return (<Container className='bg-white container-fluid p-4 mt-1'>
<Row>
<Col className='col-auto me-auto'>
<h4 className='text-dark'>{t('admin.private-title')}</h4>
<p className='text-muted'>{t('admin.private-desc')}</p>
</Col>
<Col className='col-auto d-flex align-items-center'>
<Switch toggle={toggle} state={election.restrictVote} />
</Col>
</Row>
{election.restrictVote ? <>
<ListInput onEdit={handleEmails} inputs={election.emails} validator={validateEmail} />
<Row className='text-bg-light bt-3 p-2 text-muted fw-bold'>
<Col className='col-auto'>
<FontAwesomeIcon icon={faCircleInfo} />
return (
<Container className="bg-white container-fluid p-4 mt-1">
<Row>
<Col className="col-auto me-auto">
<h4 className="text-dark">{t('admin.private-title')}</h4>
<p className="text-muted">{t('admin.private-desc')}</p>
</Col>
<Col className='col-auto d-flex align-items-center'>
{t("admin.private-tip")}
<Col className="col-auto d-flex align-items-center">
<Switch toggle={toggle} state={election.restrictVote} />
</Col>
</Row></> : null}
</Container>)
}
</Row>
{election.restrictVote ? (
<>
<ListInput
onEdit={handleEmails}
inputs={election.emails}
validator={validateEmail}
/>
<Row className="text-bg-light bt-3 p-2 text-muted fw-bold">
<Col className="col-auto">
<FontAwesomeIcon icon={faCircleInfo} />
</Col>
<Col className="col-auto d-flex align-items-center">
{t('admin.private-tip')}
</Col>
</Row>
</>
) : null}
</Container>
);
};
export default Private;

@ -1,13 +1,11 @@
/**
* This component manages the title of the election
*/
import {useElection, useElectionDispatch} from './ElectionContext';
import { useElection, useElectionDispatch } from './ElectionContext';
const TitleField = () => {
const election = useElection();
const dispatch = useElectionDispatch();
}
};
export default TitleField;

@ -1,53 +1,55 @@
import {useState} from "react";
import {
faTrashAlt,
} from "@fortawesome/free-solid-svg-icons";
import {Button, Modal, ModalHeader, ModalBody, ModalFooter} from "reactstrap";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {useTranslation} from "next-i18next";
import { useState } from 'react';
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useTranslation } from 'next-i18next';
const TrashButton = ({className, label, onClick}) => {
const TrashButton = ({ className, label, onClick }) => {
const [visibled, setVisibility] = useState(false);
const {t} = useTranslation();
const { t } = useTranslation();
const toggle = () => setVisibility(!visibled)
const toggle = () => setVisibility(!visibled);
return (
<div className="input-group-append cancelButton">
<FontAwesomeIcon onClick={toggle} icon={faTrashAlt} className="mr-2 cursorPointer" />
<FontAwesomeIcon
onClick={toggle}
icon={faTrashAlt}
className="mr-2 cursorPointer"
/>
<Modal
isOpen={visibled}
toggle={toggle}
className="modal-dialog-centered cancelForm"
>
<ModalHeader><FontAwesomeIcon icon={faTrashAlt} /></ModalHeader>
<ModalHeader>
<FontAwesomeIcon icon={faTrashAlt} />
</ModalHeader>
<ModalBody>
{t("Are you sure to delete")}{<br />}
{label && label !== "" ? (
<b>{label}</b>
) : (
<>{t("the row")}</>
)}
{t('Are you sure to delete')}
{<br />}
{label && label !== '' ? <b>{label}</b> : <>{t('the row')}</>}
</ModalBody>
<ModalFooter>
<Button
type="button"
className={className}
onClick={toggle}>
<div className="annuler"><img src="/arrow-dark-left.svg" /> {t("No")}</div>
<Button type="button" className={className} onClick={toggle}>
<div className="annuler">
<img src="/arrow-dark-left.svg" /> {t('No')}
</div>
</Button>
<Button
className="new-btn-confirm"
onClick={() => {toggle(); onClick();}}
onClick={() => {
toggle();
onClick();
}}
>
<FontAwesomeIcon icon={faTrashAlt} className="mr-2" />
{t("Yes")}
{t('Yes')}
</Button>
</ModalFooter>
</Modal>
</div >
</div>
);
}
};
export default TrashButton;

@ -1,14 +1,14 @@
import {useState} from "react";
import {faCheck} from "@fortawesome/free-solid-svg-icons";
import {Button, Modal, ModalHeader, ModalBody, ModalFooter} from "reactstrap";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {useTranslation} from "next-i18next";
import { useState } from 'react';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useTranslation } from 'next-i18next';
const VoteButtonWithConfirm = ({action}) => {
const VoteButtonWithConfirm = ({ action }) => {
const [visibled, setVisibility] = useState(false);
const {t} = useTranslation();
const { t } = useTranslation();
const toggle = () => setVisibility(!visibled)
const toggle = () => setVisibility(!visibled);
return (
<div className="input-group-append cancelButton">
@ -19,35 +19,34 @@ const VoteButtonWithConfirm = ({action}) => {
>
<div className="annuler">
<FontAwesomeIcon icon={faCheck} className="mr-2 my-auto" />
{t("Submit my vote")}
{t('Submit my vote')}
</div>
</button>
<Modal
isOpen={visibled}
toggle={toggle}
className="noRateVote"
>
<ModalHeader>{t("Attention vous navez pas votez pour tous les candidats")}</ModalHeader>
<Modal isOpen={visibled} toggle={toggle} className="noRateVote">
<ModalHeader>
{t('Attention vous navez pas votez pour tous les candidats')}
</ModalHeader>
<ModalBody>
{t("Si vous validez votre vote, les candidats sans vote auront la mention la plus basse du scrutin.")}
{t(
'Si vous validez votre vote, les candidats sans vote auront la mention la plus basse du scrutin.'
)}
<Button
className="addButton warningVote my-4"
onClick={() => {action();}}>
{t("Validez mon vote")}<img src="/arrow-white.svg" />
</Button>
<Button
className="removeButton backToVoteBtn my-4"
onClick={toggle}
onClick={() => {
action();
}}
>
{t("Revenir au vote")}
{t('Validez mon vote')}
<img src="/arrow-white.svg" />
</Button>
<Button className="removeButton backToVoteBtn my-4" onClick={toggle}>
{t('Revenir au vote')}
</Button>
</ModalBody>
</Modal>
</div >
</div>
);
}
};
export default VoteButtonWithConfirm;

@ -1,25 +1,27 @@
/* eslint react/prop-types: 0 */
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFacebookSquare } from "@fortawesome/free-brands-svg-icons";
import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFacebookSquare } from '@fortawesome/free-brands-svg-icons';
const Facebook = props => {
const Facebook = (props) => {
const handleClick = () => {
const url =
"https://www.facebook.com/sharer/sharer.php?u=" +
'https://www.facebook.com/sharer/sharer.php?u=' +
props.url +
"&t=" +
'&t=' +
props.title;
window.open(
url,
"",
"menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=500,width=700"
'',
'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=500,width=700'
);
};
return (
<img src="/facebook.svg" onClick={handleClick} className="cursorPointer mr-2" />
<img
src="/facebook.svg"
onClick={handleClick}
className="cursorPointer mr-2"
/>
);
};

@ -1,8 +1,7 @@
import PropTypes from 'prop-types';
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faCommentAlt} from "@fortawesome/free-solid-svg-icons";
import {api} from "@services/api"
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faCommentAlt } from '@fortawesome/free-solid-svg-icons';
import { api } from '@services/api';
const Gform = (props) => {
return (
@ -15,7 +14,7 @@ const Gform = (props) => {
Votre avis nous intéresse !
</a>
);
}
};
Gform.propTypes = {
className: PropTypes.string,

@ -1,19 +1,19 @@
/* eslint react/prop-types: 0 */
import React from "react";
import i18n from "../../i18n";
import React from 'react';
import i18n from '../../i18n';
const Helloasso = props => {
const Helloasso = (props) => {
const locale =
i18n.language.substring(0, 2).toLowerCase() === "fr" ? "fr" : "en";
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";
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"}
src={'/banner/' + locale + '/helloasso.png'}
alt="support us on helloasso"
style={{ width: props.width }}
/>

@ -1,21 +1,20 @@
import {useTranslation} from "next-i18next";
import {useRouter} from "next/router"
import {faPaypal} from "@fortawesome/free-brands-svg-icons";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { faPaypal } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const Paypal = () => {
const {t} = useTranslation();
const { t } = useTranslation();
// FIXME generate a xx_XX string for locale version
const {locale} = useRouter();
const { locale } = useRouter();
let localeShort = locale.substring(0, 2);
let localeComplete =
localeShort.toLowerCase() + "_" + localeShort.toUpperCase();
if (localeComplete === "en_EN") {
localeComplete = "en_US";
localeShort.toLowerCase() + '_' + localeShort.toUpperCase();
if (localeComplete === 'en_EN') {
localeComplete = 'en_US';
}
const pixelLink =
`https://www.paypal.com/${localeComplete}/i/scr/pixel.gif`;
const pixelLink = `https://www.paypal.com/${localeComplete}/i/scr/pixel.gif`;
return (
<div className="d-inline-block m-auto">
@ -27,11 +26,11 @@ const Paypal = () => {
<button
type="submit"
className="btn btn-primary"
title={t("PayPal - The safer, easier way to pay online!")}
title={t('PayPal - The safer, easier way to pay online!')}
>
{" "}
{' '}
<FontAwesomeIcon icon={faPaypal} className="mr-2" />
{t("Support us !")}
{t('Support us !')}
</button>
<input type="hidden" name="cmd" value="_s-xclick" />
<input type="hidden" name="hosted_button_id" value="KB2Z7L9KARS7C" />

@ -1,21 +1,20 @@
import {useTranslation} from "next-i18next";
import {useRouter} from "next/router"
import {faPaypal} from "@fortawesome/free-brands-svg-icons";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';
import { faPaypal } from '@fortawesome/free-brands-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
const PaypalNoLogo = () => {
const {t} = useTranslation();
const { t } = useTranslation();
// FIXME generate a xx_XX string for locale version
const {locale} = useRouter();
const { locale } = useRouter();
let localeShort = locale.substring(0, 2);
let localeComplete =
localeShort.toLowerCase() + "_" + localeShort.toUpperCase();
if (localeComplete === "en_EN") {
localeComplete = "en_US";
localeShort.toLowerCase() + '_' + localeShort.toUpperCase();
if (localeComplete === 'en_EN') {
localeComplete = 'en_US';
}
const pixelLink =
`https://www.paypal.com/${localeComplete}/i/scr/pixel.gif`;
const pixelLink = `https://www.paypal.com/${localeComplete}/i/scr/pixel.gif`;
return (
<div className="d-inline-block m-auto">
@ -27,9 +26,9 @@ const PaypalNoLogo = () => {
<button
type="submit"
className="addButton"
title={t("PayPal - The safer, easier way to pay online!")}
title={t('PayPal - The safer, easier way to pay online!')}
>
{t("Support us !")}
{t('Support us !')}
<img src="/arrow-blue.svg" className="mr-2" />
</button>
<input type="hidden" name="cmd" value="_s-xclick" />

@ -1,26 +1,27 @@
/* eslint react/prop-types: 0 */
import React from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faFacebookSquare } from "@fortawesome/free-brands-svg-icons";
import React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faFacebookSquare } from '@fortawesome/free-brands-svg-icons';
const Twitter = props => {
const Twitter = (props) => {
const handleClick = () => {
const url =
"https://twitter.com/intent/tweet?text=" +
'https://twitter.com/intent/tweet?text=' +
props.title +
"&t=" +
'&t=' +
props.url;
window.open(
url,
"",
"menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=500,width=700"
'',
'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=500,width=700'
);
};
return (
<img src="/twitter.svg" onClick={handleClick} className="cursorPointer mr-2" />
<img
src="/twitter.svg"
onClick={handleClick}
className="cursorPointer mr-2"
/>
);
};

@ -1,4 +1,4 @@
import * as React from "react";
import FlagIconFactory from "react-flag-icon-css";
import * as React from 'react';
import FlagIconFactory from 'react-flag-icon-css';
export const FlagIcon = FlagIconFactory(React, { useCssModules: false });

@ -1,12 +1,12 @@
import Link from "next/link";
import {useTranslation} from "next-i18next";
import {Button, Row, Col} from "reactstrap";
import Link from 'next/link';
import { useTranslation } from 'next-i18next';
import { Button, Row, Col } from 'reactstrap';
import Logo from '@components/Logo';
import LanguageSelector from "@components/layouts/LanguageSelector";
import LanguageSelector from '@components/layouts/LanguageSelector';
const Footer = () => {
const linkStyle = {whiteSpace: "nowrap"};
const {t} = useTranslation();
const linkStyle = { whiteSpace: 'nowrap' };
const { t } = useTranslation();
// const [bboxLink1, link1] = useBbox();
// const [bboxLink2, link2] = useBbox();
@ -17,14 +17,14 @@ const Footer = () => {
//<Col className="col-
const menu = [
{
component: (
<Logo title={false} />
)
component: <Logo title={false} />,
},
{
component: (
<Link href="/" style={linkStyle}>{t("menu.majority-judgment")}</Link>
)
<Link href="/" style={linkStyle}>
{t('menu.majority-judgment')}
</Link>
),
},
{
component: (
@ -34,60 +34,59 @@ const Footer = () => {
rel="noopener noreferrer"
style={linkStyle}
>
{t("menu.whoarewe")}
{t('menu.whoarewe')}
</Link>
)
),
},
{
component: (
<Link href="/faq" style={linkStyle}>
{t("menu.faq")}
{t('menu.faq')}
</Link>
)
),
},
{
component: (
<Link href="/" style={linkStyle}>
{t("menu.news")}
{t('menu.news')}
</Link>
)
),
},
{
component: (
<a href="mailto:app@mieuxvoter.fr?subject=[HELP]" style={linkStyle}>
{t('menu.contact-us')}
</a>
)
),
},
{
component: (
<div><LanguageSelector /></div>
)
}
]
<div>
<LanguageSelector />
</div>
),
},
];
return (
<footer>
<Row>
<Col className="col-auto me-auto">
<Row>
{menu.map((item, i) =>
{menu.map((item, i) => (
<Col key={i} className="col-auto d-flex align-items-center">
{item.component}
</Col>
)}
))}
</Row>
</Col>
<Col className="col-auto">
<Button outline={false} color="info" className='noshadow'>
<a href="/">
{t('common.support-us')}
</a>
<Button outline={false} color="info" className="noshadow">
<a href="/">{t('common.support-us')}</a>
</Button>
</Col>
</Row>
</footer >
</footer>
);
};
export default Footer;

@ -1,28 +1,21 @@
/* eslint react/prop-types: 0 */
import {useState} from "react";
import {
Collapse,
Navbar,
Nav,
NavItem,
Button,
} from "reactstrap";
import Link from "next/link";
import {useTranslation} from "next-i18next";
import LanguageSelector from "./LanguageSelector";
import { useState } from 'react';
import { Collapse, Navbar, Nav, NavItem, Button } from 'reactstrap';
import Link from 'next/link';
import { useTranslation } from 'next-i18next';
import LanguageSelector from './LanguageSelector';
const Header = () => {
const [isOpen, setOpen] = useState(false);
const toggle = () => setOpen(!isOpen);
const {t} = useTranslation("resource");
const { t } = useTranslation('resource');
return (
<header className="mobile-header">
<Navbar light className="nav-mobile" expand="lg">
<div className="navbar-header">
<Button onClick={toggle} className="navbar-toggle pt-0 mt-0">
<img src="/open-menu-icon.svg" alt="" height="50" />
</Button>
@ -35,63 +28,83 @@ const Header = () => {
<img src="/logos/logo.svg" alt="logo" height="80" />
</Link>
<Button onClick={toggle} className="navbar-toggle navbar-close-button">
<Button
onClick={toggle}
className="navbar-toggle navbar-close-button"
>
<img height="20" src="/close-menu-icon.svg" alt="logo" />
</Button>
</div>
<div>
<NavItem>
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
<Link
href="/"
onClick={toggle}
className="navbar-my-link nav-link"
>
Le jugement majoritaire
</Link>
</NavItem>
<NavItem>
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
<Link
href="/"
onClick={toggle}
className="navbar-my-link nav-link"
>
Qui sommes-nous ?
</Link>
</NavItem>
<NavItem>
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
<Link
href="/"
onClick={toggle}
className="navbar-my-link nav-link"
>
Foire aux questions
</Link>
</NavItem>
<NavItem>
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
<Link
href="/"
onClick={toggle}
className="navbar-my-link nav-link"
>
On parle de nous
</Link>
</NavItem>
<NavItem>
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
<Link
href="/"
onClick={toggle}
className="navbar-my-link nav-link"
>
Nous contactez
</Link>
</NavItem>
<NavItem>
<LanguageSelector style={{width: "80px"}} />
<LanguageSelector style={{ width: '80px' }} />
</NavItem>
</div>
<NavItem className="navbar-credits-container">
<Button className="btn-primary btn-nav">
<a href="/">
Soutenez-nous
</a>
<a href="/">Soutenez-nous</a>
</Button>
<hr />
<div className="navbar-credits sharing sharing-mobile">
<p>Partagez lapplication Mieux voter</p>
<Link href="https://www.facebook.com/mieuxvoter.fr/"><img src="/facebook.svg" className="mr-2" /></Link>
<Link href="https://twitter.com/mieux_voter"><img src="/twitter.svg" className="mr-2" /></Link>
<Link href="https://www.facebook.com/mieuxvoter.fr/">
<img src="/facebook.svg" className="mr-2" />
</Link>
<Link href="https://twitter.com/mieux_voter">
<img src="/twitter.svg" className="mr-2" />
</Link>
</div>
</NavItem>
</Nav>

@ -1,27 +1,27 @@
/* eslint react/prop-types: 0 */
import {useState} from "react";
import {Collapse, Navbar, NavbarToggler, Nav, NavItem} from "reactstrap";
import Link from "next/link";
import Head from "next/head";
import {useTranslation} from 'next-i18next'
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faRocket} from "@fortawesome/free-solid-svg-icons";
import LanguageSelector from "./LanguageSelector";
import { useState } from 'react';
import { Collapse, Navbar, NavbarToggler, Nav, NavItem } from 'reactstrap';
import Link from 'next/link';
import Head from 'next/head';
import { useTranslation } from 'next-i18next';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faRocket } from '@fortawesome/free-solid-svg-icons';
import LanguageSelector from './LanguageSelector';
const Header = () => {
const [isOpen, setOpen] = useState(false)
const [isOpen, setOpen] = useState(false);
const toggle = () => setOpen(!isOpen);
const {t} = useTranslation()
const { t } = useTranslation();
return (
<>
<Head><title>{t("title")}</title></Head>
<Head>
<title>{t('title')}</title>
</Head>
<header>
<Navbar color="light" light expand="md">
<Link href="/" className="navbar-brand">
<div className="d-flex flex-row">
<div className="align-self-center">
<img src="/logos/logo-color.svg" alt="logo" height="32" />
@ -29,8 +29,8 @@ const Header = () => {
<div className="align-self-center ml-2">
<div className="logo-text">
<h1>
{t("Voting platform")}
<small>{t("Majority Judgment")}</small>
{t('Voting platform')}
<small>{t('Majority Judgment')}</small>
</h1>
</div>
</div>
@ -40,11 +40,13 @@ const Header = () => {
<Collapse isOpen={isOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem>
<Link href="/new/" className="text-primary nav-link"> <FontAwesomeIcon icon={faRocket} className="mr-2" />
{t("Start an election")}
<Link href="/new/" className="text-primary nav-link">
{' '}
<FontAwesomeIcon icon={faRocket} className="mr-2" />
{t('Start an election')}
</Link>
</NavItem>
<NavItem style={{width: "80px"}}>
<NavItem style={{ width: '80px' }}>
<LanguageSelector />
</NavItem>
</Nav>
@ -53,6 +55,6 @@ const Header = () => {
</header>
</>
);
}
};
export default Header;

@ -1,30 +1,28 @@
import {useRouter} from 'next/router'
import { useRouter } from 'next/router';
import ReactFlagsSelect from 'react-flags-select';
const LanguageSelector = () => {
const router = useRouter();
let localeShort = router.locale.substring(0, 2).toUpperCase();
if (localeShort === "EN") localeShort = "GB";
if (localeShort === 'EN') localeShort = 'GB';
const selectHandler = e => {
const selectHandler = (e) => {
let locale = e.toLowerCase();
if (locale === "gb") locale = "en";
router.push("", "", {locale})
if (locale === 'gb') locale = 'en';
router.push('', '', { locale });
};
return (
<ReactFlagsSelect
onSelect={selectHandler}
countries={
// ["GB", "FR", "ES", "DE", "RU"]
["GB", "FR"]
['GB', 'FR']
}
selected={localeShort}
customLabels={{"GB": "English", "FR": "Francais"}}
customLabels={{ GB: 'English', FR: 'Francais' }}
className="menu-flags"
selectedSize={14}
selectedSize={14}
/>
);
};

@ -1,47 +1,50 @@
/* eslint react/prop-types: 0 */
import { useState } from "react";
import { Container, Row, Col, Nav, NavItem } from "reactstrap";
import Link from "next/link";
import Head from "next/head";
import { useTranslation } from 'next-i18next'
import { useState } from 'react';
import { Container, Row, Col, Nav, NavItem } from 'reactstrap';
import Link from 'next/link';
import Head from 'next/head';
import { useTranslation } from 'next-i18next';
export default function HeaderResultResult() {
;
return (
<Container className="sectionHeaderResult">
<Row>
<Col className="col-md-3">
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/calendar.svg" />
<p>Clos il y a 2 jours</p></Col>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol"><img src="/avatarBlue.svg" />
<p>14 votants</p></Col>
</Row>
</Col>
<Col className="sectionHeaderResultMiddleCol col-md-6">
<h3>Quel est le meilleur candidat pour les éléctions présidentielle ?</h3>
</Col>
<Col className="col-md-3">
<Row>
<Col className="sectionHeaderResultSideCol"><img src="/arrowUpload.svg" /><p>Télécharger les résultats</p></Col>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol"><img src="/arrowL.svg" /><p>Partagez les résultats</p></Col>
</Row>
</Col>
</Row>
</Container>
);
return (
<Container className="sectionHeaderResult">
<Row>
<Col className="col-md-3">
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/calendar.svg" />
<p>Clos il y a 2 jours</p>
</Col>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/avatarBlue.svg" />
<p>14 votants</p>
</Col>
</Row>
</Col>
<Col className="sectionHeaderResultMiddleCol col-md-6">
<h3>
Quel est le meilleur candidat pour les éléctions présidentielle ?
</h3>
</Col>
<Col className="col-md-3">
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/arrowUpload.svg" />
<p>Télécharger les résultats</p>
</Col>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/arrowL.svg" />
<p>Partagez les résultats</p>
</Col>
</Row>
</Col>
</Row>
</Container>
);
}

@ -1,38 +1,29 @@
/* eslint react/prop-types: 0 */
import { useState } from "react";
import { Container, Row, Col, Nav, NavItem } from "reactstrap";
import Link from "next/link";
import Head from "next/head";
import { useTranslation } from 'next-i18next'
import { useState } from 'react';
import { Container, Row, Col, Nav, NavItem } from 'reactstrap';
import Link from 'next/link';
import Head from 'next/head';
import { useTranslation } from 'next-i18next';
export default function HeaderMobileResult() {
;
return (
<Container className="sectionHeaderResult">
<Row className="sectionHeaderResultMiddleCol">
<h3>Quel est le meilleur candidat pour les éléctions présidentielle ?</h3>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/calendar.svg" />
<p>Clos il y a 2 jours</p>
</Col>
<Col className="sectionHeaderResultSideCol">
<img src="/avatarBlue.svg" />
<p>14 votants</p>
</Col>
</Row>
</Container >
);
return (
<Container className="sectionHeaderResult">
<Row className="sectionHeaderResultMiddleCol">
<h3>
Quel est le meilleur candidat pour les éléctions présidentielle ?
</h3>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/calendar.svg" />
<p>Clos il y a 2 jours</p>
</Col>
<Col className="sectionHeaderResultSideCol">
<img src="/avatarBlue.svg" />
<p>14 votants</p>
</Col>
</Row>
</Container>
);
}

@ -1,14 +1,11 @@
import React from 'react';
import HeaderDesktopResult from './HeaderDesktopResult';
import HeaderMobileResult from './HeaderMobileResult';
import { useMediaQuery } from "react-responsive";
import { useMediaQuery } from 'react-responsive';
export default function HeaderResult() {
const isMobile = useMediaQuery({ query: '(max-width: 800px)' });
const isMobile = useMediaQuery({ query: "(max-width: 800px)" });
if (isMobile) return <HeaderMobileResult />;
else return <HeaderDesktopResult />;
}
if (isMobile) return <HeaderMobileResult />;
else return <HeaderDesktopResult />;
}

@ -4,17 +4,17 @@ import { useRef } from 'react';
import { useEffect } from 'react';
export const useBbox = () => {
const ref = useRef();
const [bbox, setBbox] = useState({});
const ref = useRef();
const [bbox, setBbox] = useState({});
const set = () =>
setBbox(ref && ref.current ? ref.current.getBoundingClientRect() : {});
const set = () =>
setBbox(ref && ref.current ? ref.current.getBoundingClientRect() : {});
useEffect(() => {
set();
window.addEventListener('resize', set);
return () => window.removeEventListener('resize', set);
}, []);
useEffect(() => {
set();
window.addEventListener('resize', set);
return () => window.removeEventListener('resize', set);
}, []);
return [bbox, ref];
};
return [bbox, ref];
};

@ -1,12 +1,10 @@
import React from "react";
import Image from 'next/image'
import React from 'react';
import Image from 'next/image';
const Loader = () => {
return (
<div className="loader bg-primary">
<img src="/loader-pulse-2.gif" alt="Loading..." />
</div>
);
};

@ -13,6 +13,6 @@ const plot = () => (
// ]}
// layout={{width: 1000, height: 500, title: 'Nombre de voix par candidat'}}
// />
)
);
export default plot;

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

@ -1,41 +1,44 @@
import Head from 'next/head'
import '@styles/globals.css'
import '@styles/loader.css'
import "@styles/scss/config.scss";
import '@fortawesome/fontawesome-svg-core/styles.css'
import Head from 'next/head';
import '@styles/globals.css';
import '@styles/loader.css';
import '@styles/scss/config.scss';
import '@fortawesome/fontawesome-svg-core/styles.css';
// import nextI18NextConfig from '../next-i18next.config.js'
import {appWithTranslation} from 'next-i18next'
import {AppProvider} from '@services/context'
import Header from '@components/layouts/Header'
import Footer from '@components/layouts/Footer'
import { appWithTranslation } from 'next-i18next';
import { AppProvider } from '@services/context';
import Header from '@components/layouts/Header';
import Footer from '@components/layouts/Footer';
function Application({Component, pageProps}) {
const origin = typeof window !== 'undefined' && window.location.origin ? window.location.origin : 'http://localhost';
return (<AppProvider><Head>
<link rel="icon" key="favicon" href="/favicon.ico" />
<meta property="og:type" content="website" key="og:type" />
<meta property="og:url" content={origin} key="og:url" />
<meta
property="og:image"
content="https://app.mieuxvoter.fr/app-mieux-voter.png"
key="og:image"
/>
</Head>
<main className='d-flex flex-column justify-content-between'>
<div className='d-flex flex-grow-1 justify-content-center'>
<Header />
<div className="d-flex flex-column w-100 align-items-start">
<Component {...pageProps} />
function Application({ Component, pageProps }) {
const origin =
typeof window !== 'undefined' && window.location.origin
? window.location.origin
: 'http://localhost';
return (
<AppProvider>
<Head>
<link rel="icon" key="favicon" href="/favicon.ico" />
<meta property="og:type" content="website" key="og:type" />
<meta property="og:url" content={origin} key="og:url" />
<meta
property="og:image"
content="https://app.mieuxvoter.fr/app-mieux-voter.png"
key="og:image"
/>
</Head>
<main className="d-flex flex-column justify-content-between">
<div className="d-flex flex-grow-1 justify-content-center">
<Header />
<div className="d-flex flex-column w-100 align-items-start">
<Component {...pageProps} />
</div>
</div>
</div>
<Footer />
</main>
</AppProvider>);
<Footer />
</main>
</AppProvider>
);
}
export default appWithTranslation(Application)
export default appWithTranslation(Application);

@ -1,28 +1,28 @@
import { createRef } from "react";
import Head from "next/head";
import { useRouter } from "next/router";
import { useTranslation } from "next-i18next";
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
import { createRef } from 'react';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import {
getDetails,
apiErrors,
ELECTION_NOT_STARTED_ERROR,
} from "@services/api";
import { Col, Container, Row, Button } from "reactstrap";
import Link from "next/link";
} from '@services/api';
import { Col, Container, Row, Button } from 'reactstrap';
import Link from 'next/link';
import {
faCopy,
faVoteYea,
faExclamationTriangle,
faExternalLinkAlt,
faPollH,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import CopyField from "@components/CopyField";
import Error from "@components/Error";
import Facebook from "@components/banner/Facebook";
import Twitter from "@components/banner/Twitter";
import config from "../../../next-i18next.config.js";
} from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import CopyField from '@components/CopyField';
import Error from '@components/Error';
import Facebook from '@components/banner/Facebook';
import Twitter from '@components/banner/Twitter';
import config from '../../../next-i18next.config.js';
import { AnimatePresence, motion } from 'framer-motion';
export async function getServerSideProps({ query: { pid }, locale }) {
let [details, translations] = await Promise.all([
@ -67,9 +67,9 @@ const ConfirmElection = ({
}
const origin =
typeof window !== "undefined" && window.location.origin
typeof window !== 'undefined' && window.location.origin
? window.location.origin
: "http://localhost";
: 'http://localhost';
const urlVote = new URL(`/vote/${pid}`, origin);
const urlResult = new URL(`/result/${pid}`, origin);
@ -77,13 +77,12 @@ const ConfirmElection = ({
<>
<p className="mb-1">
{t(
"Voters received a link to vote by email. Each link can be used only once!"
'Voters received a link to vote by email. Each link can be used only once!'
)}
</p>
</>
) : (
<>
<CopyField
value={urlVote.href}
iconCopy={faCopy}
@ -95,17 +94,17 @@ const ConfirmElection = ({
const fb = invitationOnly ? null : (
<Facebook
className="btn btn-sm btn-outline-light m-2"
text={t("Share election on Facebook")}
text={t('Share election on Facebook')}
url={urlVote}
title={"app.mieuxvoter.fr"}
title={'app.mieuxvoter.fr'}
/>
);
const tw = invitationOnly ? null : (
<Twitter
className="btn btn-sm btn-outline-light m-2"
text={t("Share election on Twitter")}
text={t('Share election on Twitter')}
url={urlVote}
title={"app.mieuxvoter.fr"}
title={'app.mieuxvoter.fr'}
/>
);
const participate = invitationOnly ? null : (
@ -114,7 +113,7 @@ const ConfirmElection = ({
<Link href={`/vote/${pid}`}>
<a target="_blank" rel="noreferrer" className="btn btn-success">
<FontAwesomeIcon icon={faVoteYea} className="mr-2" />
{t("resource.participateBtn")}
{t('resource.participateBtn')}
</a>
</Link>
</Col>
@ -124,35 +123,34 @@ const ConfirmElection = ({
return (
<Container className="full-height-container">
<Head>
<title>{t("Successful election creation!")}</title>
<title>{t('Successful election creation!')}</title>
<link rel="icon" href="/favicon.ico" />
<meta key="og:title" property="og:title" content={title} />
<meta
property="og:description"
key="og:description"
content={t("common.application")}
content={t('common.application')}
/>
</Head>
<motion.div
className="mx-auto row"
initial={{ scale: 1, paddingBottom: '200px' }}
animate={{ scale: 0.5, paddingBottom: '0px' }}
transition={{
type: "spring",
damping: 100,
delay: 4
}}
>
<motion.div
className="mx-auto row"
initial={{ scale: 1, paddingBottom: '200px' }}
animate={{ scale: 0.5, paddingBottom: '0px' }}
transition={{
type: 'spring',
damping: 100,
delay: 4,
}}
>
<motion.div
className="main-animation"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
type: "spring",
type: 'spring',
damping: 20,
delay: 1
delay: 1,
}}
>
<img src="/urne-vide.svg" />
@ -161,13 +159,12 @@ const ConfirmElection = ({
initial={{ scale: 0, y: 50 }}
animate={{ scale: 1, y: 0 }}
transition={{
type: "spring",
type: 'spring',
stiffness: 260,
damping: 20,
delay: 2
damping: 20,
delay: 2,
}}
>
<img src="/urne-letter.svg" />
</motion.div>
<motion.div
@ -175,55 +172,53 @@ const ConfirmElection = ({
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{
type: "spring",
type: 'spring',
damping: 20,
delay: 3
delay: 3,
}}
>
<img className="urne-fronjt" src="/urne-star.svg" />
</motion.div>
<img className="urne-front" src="/urne-front.svg" />
</motion.div></motion.div>
</motion.div>
</motion.div>
<motion.div
className=""
initial={{ scale: 0, opacity: 0, y: 100 }}
animate={{ scale: 1, opacity: 1, y: -70 }}
transition={{
type: "spring",
damping: 100,
delay: 4
}}
>
<Row className="mt-5">
<Col className="text-center mx-auto success-title" lg="3">
<h2>{t("Successful election creation!")}</h2>
</Col>
</Row>
className=""
initial={{ scale: 0, opacity: 0, y: 100 }}
animate={{ scale: 1, opacity: 1, y: -70 }}
transition={{
type: 'spring',
damping: 100,
delay: 4,
}}
>
<Row className="mt-5">
<Col className="text-center mx-auto success-title" lg="3">
<h2>{t('Successful election creation!')}</h2>
</Col>
</Row>
<Row className="mb-4">
<Col className=" mx-auto" lg="4">
<div className="p-4 pb-5">
<CopyField
value={urlVote}
iconCopy={faCopy}
iconOpen={faExternalLinkAlt}
text={t("Copier le lien du vote")}
/>
<CopyField
value={urlResult}
iconCopy={faCopy}
iconOpen={faExternalLinkAlt}
text={t("Copier le lien des résultats")}
/>
</div>
</Col>
</Row>
<Row className="mt-4 mb-4 justify-content-center">
{/* {participate}
<Row className="mb-4">
<Col className=" mx-auto" lg="4">
<div className="p-4 pb-5">
<CopyField
value={urlVote}
iconCopy={faCopy}
iconOpen={faExternalLinkAlt}
text={t('Copier le lien du vote')}
/>
<CopyField
value={urlResult}
iconCopy={faCopy}
iconOpen={faExternalLinkAlt}
text={t('Copier le lien des résultats')}
/>
</div>
</Col>
</Row>
<Row className="mt-4 mb-4 justify-content-center">
{/* {participate}
<Col className="text-center col-lg-3">
<Link href={`/result/${pid}`}>
<a target="_blank" rel="noreferrer" className="btn btn-secondary">
@ -232,15 +227,18 @@ const ConfirmElection = ({
</a>
</Link>
</Col> */}
<Button className="cursorPointer btn-validation mb-5" >{t("Administrez le vote")}<img src="/arrow-white.svg" /></Button>
</Row>
<Row className="mt-5">
<Col className="text-center offset-lg-3" lg="6">
{fb}
{tw}
{t("Partagez lélection")}
</Col>
</Row>
<Button className="cursorPointer btn-validation mb-5">
{t('Administrez le vote')}
<img src="/arrow-white.svg" />
</Button>
</Row>
<Row className="mt-5">
<Col className="text-center offset-lg-3" lg="6">
{fb}
{tw}
{t('Partagez lélection')}
</Col>
</Row>
</motion.div>
</Container>
);

@ -1,22 +1,22 @@
import {useState} from "react";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import CandidatesField from "@components/admin/CandidatesField";
import ParamsField from "@components/admin/ParamsField";
import ConfirmField from "@components/admin/ConfirmField";
import {ElectionProvider, useElection} from '@components/admin/ElectionContext';
import {ProgressSteps, creationSteps} from "@components/CreationSteps";
import {GetStaticProps} from "next";
import { useState } from 'react';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import CandidatesField from '@components/admin/CandidatesField';
import ParamsField from '@components/admin/ParamsField';
import ConfirmField from '@components/admin/ConfirmField';
import {
ElectionProvider,
useElection,
} from '@components/admin/ElectionContext';
import { ProgressSteps, creationSteps } from '@components/CreationSteps';
import { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async ({locale}) => ({
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['resource'])),
},
});
/**
/**
* Manage the steps for creating an election
*/
const CreateElectionForm = () => {
@ -25,11 +25,11 @@ const CreateElectionForm = () => {
const handleSubmit = () => {
if (stepId < creationSteps.length - 1) {
setStepId(i => i + 1);
setStepId((i) => i + 1);
} else {
// TODO
}
else { // TODO
}
}
};
// at which creation step are we?
const [stepId, setStepId] = useState(2);
@ -37,29 +37,28 @@ const CreateElectionForm = () => {
let Step: JSX.Element;
if (step == 'candidate') {
Step = <CandidatesField onSubmit={handleSubmit} />
Step = <CandidatesField onSubmit={handleSubmit} />;
} else if (step == 'params') {
Step = <ParamsField onSubmit={handleSubmit} />;
} else if (step == 'confirm') {
Step = <ConfirmField
onSubmit={handleSubmit}
goToCandidates={() => setStepId(0)}
goToParams={() => setStepId(1)}
/>;
Step = (
<ConfirmField
onSubmit={handleSubmit}
goToCandidates={() => setStepId(0)}
goToParams={() => setStepId(1)}
/>
);
} else {
throw Error(`Unknown step ${step}`);
}
return (
<ElectionProvider>
<ProgressSteps step={step} />
{Step}
</ElectionProvider>
);
}
};
// <Container className="addCandidatePage">
// { //<ToastContainer />
// }
@ -78,27 +77,27 @@ const CreateElectionForm = () => {
// <Col className="stepFormCol opacity">
// <img src="/icone-three-dark.svg" />
// <h4>Confirmation</h4>
//
//
// </Col>
// </Row>
//
//
// <Row>
// <Col xs="12">
// <CandidatesField onChange={setCandidates} />
// </Col>
// </Row>
//
//
// <Row className="justify-content-center">
// <div className="mx-auto mt-5">
//
//
// <Button onClick={handleFirstSubmit} className="cursorPointer btn-opacity btn-validation mb-5" >{t("Confirm")}<img src="/arrow-white.svg" /></Button>
// </div>
// </Row>
// </div>
//
//
//
//
//
//
//
//
// <div className={displayBlock}>
// <div onClick={changeDisplay} className="btn-return-candidates"><FontAwesomeIcon icon={faArrowLeft} className="mr-2" />Retour aux candidats</div>
// <Row className="stepForm">
@ -113,7 +112,7 @@ const CreateElectionForm = () => {
// <Col className="stepFormCol opacity">
// <img src="/icone-three-dark.svg" />
// <h4>Confirmation</h4>
//
//
// </Col>
// </Row>
// <div className="settings-modal-body ">
@ -123,7 +122,7 @@ const CreateElectionForm = () => {
// <Row className="row-label">
// <Col xs="10" lg="10">
// <Label htmlFor="title">{t("Access to results")} {t("Immediately")}</Label>
//
//
// </Col>
// <Col l xs="2" lg="2">
// <div className="radio-group">
@ -161,7 +160,7 @@ const CreateElectionForm = () => {
// <hr className="settings-divider" />
// <div className="row-label" id="voting-time-label">
// <Row>
//
//
// <Col xs="10">
// <Label htmlFor="title">{t("Voting time")}</Label>
// </Col>
@ -189,7 +188,7 @@ const CreateElectionForm = () => {
// <div className="radio-switch"></div>
// </div>
// </Col>
//
//
// </Row>
// <div
// className={
@ -203,7 +202,7 @@ const CreateElectionForm = () => {
// <span className="label">- {t("Starting date")}</span>
// </Col>
// <Col xs="6" md="4" lg="3">
//
//
// <input
// className="form-control"
// type="date"
@ -235,9 +234,9 @@ const CreateElectionForm = () => {
// </select>
// </Col>
// </Row>
//
//
// <Row className="mt-2">
//
//
// <Col xs="6" md="4" lg="3" className="time-container">
// <input
// className="form-control"
@ -278,14 +277,14 @@ const CreateElectionForm = () => {
// <Row className="row-label">
// <Label>{t("Grades")}</Label>
// <FontAwesomeIcon onClick={toggleGrades} icon={faChevronRight} className="ml-2 my-auto" />
//
//
// </Row>
// <Row className="mx-0">
// <p className="m-0">{t("You can select here the number of grades for your election")}</p>
// </Row>
// </Col>
// </Row>
//
//
// <Modal
// isOpen={visibledGrades}
// toggle={toggleGrades}
@ -296,9 +295,9 @@ const CreateElectionForm = () => {
// <ModalBody>
// <p>{t("Choisissez le nombre de mentions des votes.")}</p>
// <div className="numGradesContainer justify-content-center" tabIndex={candidates.length + 3}>
//
//
// {badgesValues.map(f => (
//
//
// <Label className="numGrades numGradesMobile">
// <Input type="radio" name="radio" value={f} checked={badgesValue === f}
// onChange={e => setNumGrades(e.currentTarget.value)} />
@ -331,11 +330,11 @@ const CreateElectionForm = () => {
// {t("Valider les mentions")}
// </Button>
// </Row>
//
//
// </ModalBody>
// </Modal>
//
//
//
//
// <Row className="componentDesktop">
// <Col xs="9">
// <Label>{t("Grades")}</Label>
@ -344,14 +343,14 @@ const CreateElectionForm = () => {
// <Col xs="3">
// <div className="numGradesContainer justify-content-end" tabIndex={candidates.length + 3}>
// {badgesValues.map(f => (
//
//
// <Label className="numGrades ">
// <Input type="radio" name="radio" value={f} checked={badgesValue === f}
// onChange={e => setNumGrades(e.currentTarget.value)} />
// <div className="customCheckmarck"><p>{f}</p></div>
// </Label>
// ))}
//
//
// </div>
// </Col>
// <Col
@ -382,7 +381,7 @@ const CreateElectionForm = () => {
// <Row className="row-label">
// <Col xs="10" lg="10">
// <Label htmlFor="title">{t("Vote privée")}</Label>
//
//
// </Col>
// <Col l xs="2" lg="2">
// <div className="radio-group">
@ -410,7 +409,7 @@ const CreateElectionForm = () => {
// </div>
// </Col>
// </Row>
//
//
// <Row className="mx-0"><p className="mx-0">{t("Uniquement les personnes invités par mail pourront participé au vote")}</p></Row>
// </Col></Row>
// <hr className="settings-divider" />
@ -432,7 +431,7 @@ const CreateElectionForm = () => {
// <ModalBody>
// <Row>
// <p className="mr-2 my-auto">{t("À ")}</p>
//
//
// { /* <ReactMultiEmail
// placeholder={t("Add here participants' emails")}
// emails={emails}
@ -450,12 +449,12 @@ const CreateElectionForm = () => {
// >
// ×
// </span>
//
//
// </div>
//
//
// );
// }}
//
//
// />
// */}
// </Row>
@ -470,9 +469,9 @@ const CreateElectionForm = () => {
// </Row>
// </ModalBody>
// </Modal>
//
//
//
//
//
//
// </Col>
// </Row>
// <Row className="componentDesktop">
@ -510,7 +509,7 @@ const CreateElectionForm = () => {
// </Row>
// <hr className="settings-divider" />
// <Col xs="12" md="3">
//
//
// </Col>
// </div>
// <div className="justify-content-center">
@ -533,7 +532,7 @@ const CreateElectionForm = () => {
// <div>
// <Button onClick={handleSendNotReady} className="mt-5 componentDesktop btn-transparent cursorPointer btn-validation mb-5 mx-auto" >{t("Confirm")}<img src="/arrow-white.svg" /></Button>
// <Button
//
//
// className="componentMobile btn-confirm-mobile mb-5"
// onClick={handleSendNotReady}>
// <FontAwesomeIcon className="mr-2" icon={faCheck} />
@ -551,16 +550,16 @@ const CreateElectionForm = () => {
export default CreateElectionForm;
//
// const handleIsTimeLimited = (event) => {
// setTimeLimited(event.target.value === "1");
// };
// const handleIsTimeLimited = (event) => {
// setTimeLimited(event.target.value === "1");
// };
// const handleRestrictResultCheck = (event) => {
// setRestrictResult(event.target.value === "1");
// };
// const handleRestrictVote = (event) => {
// setRestrictVote(event.target.value === "1");
// };
// const handleRestrictResultCheck = (event) => {
// setRestrictResult(event.target.value === "1");
// };
// const handleRestrictVote = (event) => {
// setRestrictVote(event.target.value === "1");
// };
//
// import {ReactMultiEmail, isEmail} from "react-multi-email";
// import "react-multi-email/style.css";
@ -568,11 +567,11 @@ export default CreateElectionForm;
// import "react-toastify/dist/ReactToastify.css";
//
//
//
//
// // Retrieve the day and remove the time. Return a Date
// const dateMinusTime = (date) =>
// new Date(getOnlyValidDate(date).getTime() - time(getOnlyValidDate(date)));
//
//
// const displayClockOptions = () =>
// Array(24)
// .fill(1)
@ -582,14 +581,14 @@ export default CreateElectionForm;
// </option>
// ));
//
// switch (action.type) {
// case 'title': {
// election.title = action.value;
// return election;
// }
// case 'time': {
// election.endTime = action.value;
// return election;
// }
//
// }
// switch (action.type) {
// case 'title': {
// election.title = action.value;
// return election;
// }
// case 'time': {
// election.endTime = action.value;
// return election;
// }
//
// }

@ -1,8 +1,8 @@
import {useState, useEffect} from "react";
import Head from "next/head";
import {useRouter} from "next/router";
import {useTranslation} from "next-i18next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import { useState, useEffect } from 'react';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import {
Collapse,
Container,
@ -15,9 +15,13 @@ import {
Button,
Card,
CardBody,
Modal, ModalHeader, ModalBody, ModalFooter, CustomInput
} from "reactstrap";
import {GetStaticProps} from "next";
Modal,
ModalHeader,
ModalBody,
ModalFooter,
CustomInput,
} from 'reactstrap';
import { GetStaticProps } from 'next';
// import {ReactMultiEmail, isEmail} from "react-multi-email";
// import "react-multi-email/style.css";
// import {toast, ToastContainer} from "react-toastify";
@ -42,18 +46,17 @@ import {GetStaticProps} from "next";
// import ConfirmModal from "@components/admin/ConfirmModal";
// import config from "../../next-i18next.config.js";
// Error messages
// const AT_LEAST_2_CANDIDATES_ERROR = "Please add at least 2 candidates.";
// const NO_TITLE_ERROR = "Please add a title.";
//
//
// const isValidDate = (date) => date instanceof Date && !isNaN(date);
// const getOnlyValidDate = (date) => (isValidDate(date) ? date : new Date());
//
//
// // Convert a Date object into YYYY-MM-DD
// const dateToISO = (date) =>
// getOnlyValidDate(date).toISOString().substring(0, 10);
//
//
// // Retrieve the current hour, minute, sec, ms, time into a timestamp
// const hours = (date) => getOnlyValidDate(date).getHours() * 3600 * 1000;
// const minutes = (date) => getOnlyValidDate(date).getMinutes() * 60 * 1000;
@ -64,14 +67,14 @@ import {GetStaticProps} from "next";
// minutes(getOnlyValidDate(date)) +
// seconds(getOnlyValidDate(date)) +
// ms(getOnlyValidDate(date));
//
//
// // Retrieve the time part from a timestamp and remove the day. Return a int.
// const timeMinusDate = (date) => time(getOnlyValidDate(date));
//
//
// // Retrieve the day and remove the time. Return a Date
// const dateMinusTime = (date) =>
// new Date(getOnlyValidDate(date).getTime() - time(getOnlyValidDate(date)));
//
//
// const displayClockOptions = () =>
// Array(24)
// .fill(1)
@ -81,7 +84,7 @@ import {GetStaticProps} from "next";
// </option>
// ));
export const getStaticProps: GetStaticProps = async ({locale}) => ({
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['resource'])),
},
@ -212,9 +215,8 @@ const CreateElection = (props) => {
const [showModal, setShowModal] = useState(false);
return (
<p>FOO</p>);
}
return <p>FOO</p>;
};
// <Container className="addCandidatePage">
// <Head>
// <meta
@ -243,10 +245,10 @@ const CreateElection = (props) => {
// <Col className="stepFormCol">
// <img src="/icone-three-dark.svg" />
// <h4>Confirmation</h4>
//
//
// </Col>
// </Row>
//
//
// <div className="settings-modal-body">
// <Row>
// <Col xs="10" lg="10">
@ -316,7 +318,7 @@ const CreateElection = (props) => {
// </select>
// </Col>
// </Row>
//
//
// <Row className="mt-2">
// <Col xs="12" md="3" lg="3">
// <span className="label">- {t("Ending date")}</span>
@ -450,7 +452,7 @@ const CreateElection = (props) => {
// </Row>
// <hr className="mt-2 mb-2" />
// <Col xs="12" md="3">
//
//
// </Col>
// </div>
// <div className="justify-content-center">
@ -470,13 +472,13 @@ const CreateElection = (props) => {
// confirmCallback={handleSubmit}
// />
// ) : (
//
//
// <Button onClick={toggle} className="cursorPointer btn-validation mb-5" >{t("Confirm")}<img src="/arrow-white.svg" /></Button>
//
//
// )}
// </div></form>
//
//
//
//
// </Container >
// );
// };

@ -1,19 +1,19 @@
import Link from "next/link";
import {Container, Row, Col} from "reactstrap";
import {useTranslation} from "next-i18next";
import Paypal from "@components/banner/Paypal";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import config from "../next-i18next.config.js";
import {GetStaticProps} from "next";
import Link from 'next/link';
import { Container, Row, Col } from 'reactstrap';
import { useTranslation } from 'next-i18next';
import Paypal from '@components/banner/Paypal';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import config from '../next-i18next.config.js';
import { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async ({locale}) => ({
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, [], config)),
},
});
const FAQ = () => {
const {t} = useTranslation();
const { t } = useTranslation();
return (
<Container>
<Row>
@ -23,7 +23,7 @@ const FAQ = () => {
</Row>
<Row className="mt-4">
<Col className="text-center">
<h1>{t("FAQ")}</h1>
<h1>{t('FAQ')}</h1>
</Col>
</Row>
<Row className="mt-4">
@ -38,7 +38,7 @@ const FAQ = () => {
lélectorat (celui qui obtient la meilleure mention « majoritaire
»).
</p>
<div style={{maxWidth: "445px"}}>
<div style={{ maxWidth: '445px' }}>
<video width="100%" height="250" controls="controls">
<source
src="/video/Le_Jugement_Majoritaire_en_1_minute.mp4"
@ -106,7 +106,7 @@ const FAQ = () => {
<p>
Cette application ne convient pas pour les votes à plus de 1000
votants. Si cest votre cas, nous vous invitons à nous contacter par
email à ladresse{" "}
email à ladresse{' '}
<a href="mailto:contact@mieuxvoter.fr" className="text-light">
contact@mieuxvoter.fr
</a>

@ -1,30 +1,28 @@
import {useState} from "react";
import Link from "next/link";
import Image from "next/image";
import {GetStaticProps} from 'next';
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import {useTranslation} from "next-i18next";
import {Container, Row, Col, Button, Input} from "reactstrap";
import { useState } from 'react';
import Link from 'next/link';
import Image from 'next/image';
import { GetStaticProps } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
import { Container, Row, Col, Button, Input } from 'reactstrap';
import Logo from '@components/Logo';
import {CREATE_ELECTION} from '@services/routes';
import ballotBox from '../public/urne.svg'
import email from '../public/email.svg'
import respect from '../public/respect.svg'
import vote from '../public/vote.svg'
import twitter from '../public/twitter.svg'
import facebook from '../public/facebook.svg'
import arrowRight from '../public/arrow-white.svg'
export const getStaticProps: GetStaticProps = async ({locale}) => ({
import { CREATE_ELECTION } from '@services/routes';
import ballotBox from '../public/urne.svg';
import email from '../public/email.svg';
import respect from '../public/respect.svg';
import vote from '../public/vote.svg';
import twitter from '../public/twitter.svg';
import facebook from '../public/facebook.svg';
import arrowRight from '../public/arrow-white.svg';
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['resource'])),
},
});
const StartForm = () => {
const {t} = useTranslation('resource');
const { t } = useTranslation('resource');
const [title, setTitle] = useState(null);
return (
@ -35,39 +33,36 @@ const StartForm = () => {
<Logo height="128" />
</Row>
<Row>
<h4>{t("home.motto")}</h4>
<h4>{t('home.motto')}</h4>
</Row>
<Row>
<h2>{t("home.slogan")}</h2>
<h2>{t('home.slogan')}</h2>
</Row>
<Row className="justify-content-end">
<Input
placeholder={t("home.writeQuestion")}
placeholder={t('home.writeQuestion')}
autoFocus
required
className="mt-2 mb-0 sectionOneHomeInput"
name="title"
value={title ? title : ""}
value={title ? title : ''}
onChange={(e) => setTitle(e.target.value)}
maxLength="250"
/>
<p className="pt-0 mt-0 mr-0 maxLength">250</p>
</Row>
<Row>
<Link href={{pathname: CREATE_ELECTION, query: {title: title}}}>
<Link href={{ pathname: CREATE_ELECTION, query: { title: title } }}>
<Button color="secondary" outline={true} type="submit">
<Row className="justify-content-md-center p-2">
<Col className='col-auto'>
{t("home.start")}
</Col>
<Col className='col-auto d-flex'>
<Col className="col-auto">{t('home.start')}</Col>
<Col className="col-auto d-flex">
<Image
src={arrowRight}
width={22}
height={22}
alt={t("home.start")}
alt={t('home.start')}
className="align-self-center"
/>
</Col>
@ -76,60 +71,57 @@ const StartForm = () => {
</Link>
</Row>
<Row className="noAds">
<p>{t("home.noAds")}</p>
<p>{t('home.noAds')}</p>
</Row>
</Col>
<Col></Col>
</Row>
</form>
);
}
};
const AdvantagesRow = () => {
const {t} = useTranslation('resource');
const { t } = useTranslation('resource');
const resources = [
{
"src": ballotBox,
"alt": t("home.alt-icon-ballot-box"),
"title": t('home.advantage-1-title'),
"desc": t('home.advantage-1-desc'),
src: ballotBox,
alt: t('home.alt-icon-ballot-box'),
title: t('home.advantage-1-title'),
desc: t('home.advantage-1-desc'),
},
{
"src": email,
"alt": t("home.alt-icon-envelop"),
"title": t('home.advantage-2-title'),
"desc": t('home.advantage-2-desc'),
src: email,
alt: t('home.alt-icon-envelop'),
title: t('home.advantage-2-title'),
desc: t('home.advantage-2-desc'),
},
{
"src": respect,
"alt": t("home.alt-icon-respect"),
"title": t('home.advantage-3-title'),
"desc": t('home.advantage-3-desc'),
}
]
return (<Row className="sectionTwoRowOne">
{resources.map((item, i) =>
<Col key={i} className="sectionTwoRowOneCol">
<Image
src={item.src}
alt={item.alt}
height="128"
className="d-block mx-auto"
/>
<h4>{item.title}</h4>
<p>{item.desc}</p>
</Col>
)
}
</Row >
)
}
src: respect,
alt: t('home.alt-icon-respect'),
title: t('home.advantage-3-title'),
desc: t('home.advantage-3-desc'),
},
];
return (
<Row className="sectionTwoRowOne">
{resources.map((item, i) => (
<Col key={i} className="sectionTwoRowOneCol">
<Image
src={item.src}
alt={item.alt}
height="128"
className="d-block mx-auto"
/>
<h4>{item.title}</h4>
<p>{item.desc}</p>
</Col>
))}
</Row>
);
};
const ExperienceRow = () => {
const {t} = useTranslation('resource');
const { t } = useTranslation('resource');
return (
<Row className="sectionTwoRowTwo">
<Row className="sectionTwoHomeImage">
@ -151,61 +143,56 @@ const ExperienceRow = () => {
</Row>
<Row className="sectionTwoRowThreeCol mt-5">
<Col>
<Button
color="primary"
className="p-4 fs-5"
>
<Button color="primary" className="p-4 fs-5">
{t('home.experience-call-to-action')}
<Image src={arrowRight} width={22} height={22} className="mr-2" />
</Button>
</Col>
</Row>
</Row>
)
);
};
const ShareRow = () => {
const {t} = useTranslation('resource');
const { t } = useTranslation('resource');
return (
<Row className="sharing justify-content-md-center">
<Col className="col-auto">
{t('home.share')}
</Col>
<Col className="col-auto">{t('home.share')}</Col>
<Col className="col-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="https://www.facebook.com/mieuxvoter.fr/">
<Image height={22} width={22}
src={facebook} />
href="https://www.facebook.com/mieuxvoter.fr/"
>
<Image height={22} width={22} src={facebook} />
</a>
</Col>
<Col className="col-auto">
<a
target="_blank"
rel="noopener noreferrer"
href="https://twitter.com/mieux_voter">
<Image height={22} width={22}
src={twitter} />
href="https://twitter.com/mieux_voter"
>
<Image height={22} width={22} src={twitter} />
</a>
</Col>
</Row>
)
}
);
};
const Home = () => {
const {t} = useTranslation('resource');
const { t } = useTranslation('resource');
return (
<Container fluid={true} className='p-0'>
<section><StartForm /></section>
<Container fluid={true} className="p-0">
<section>
<StartForm />
</section>
<section className="sectionTwoHome">
<AdvantagesRow />
<ExperienceRow />
<ShareRow />
</section>
</Container >
</Container>
);
};

@ -1,18 +1,18 @@
import Link from "next/link";
import {Container, Row, Col} from "reactstrap";
import {useTranslation} from "next-i18next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import config from "../next-i18next.config.js";
import {GetStaticProps} from "next";
import Link from 'next/link';
import { Container, Row, Col } from 'reactstrap';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import config from '../next-i18next.config.js';
import { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async ({locale}) => ({
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, [], config)),
},
});
const LegalNotices = (props) => {
const {t} = useTranslation();
const { t } = useTranslation();
return (
<Container>
<Row>
@ -22,14 +22,14 @@ const LegalNotices = (props) => {
</Row>
<Row className="mt-4">
<Col className="text-center">
<h1>{t("resource.legalNotices")}</h1>
<h1>{t('resource.legalNotices')}</h1>
</Col>
</Row>
<Row className="mt-4">
<Col>
<h3 className="bold">Editeur</h3>
<p>
Cette Application est éditée par lassociation loi 1901{" "}
Cette Application est éditée par lassociation loi 1901{' '}
<a
href="https://mieuxvoter.fr/"
target="_blank"
@ -43,7 +43,7 @@ const LegalNotices = (props) => {
</p>
<p>
Adresse email :{" "}
Adresse email :{' '}
<a href="mailto:app@mieuxvoter.fr" className="text-light">
app@mieuxvoter.fr
</a>
@ -71,7 +71,7 @@ const LegalNotices = (props) => {
<Row className="mt-4">
<Col className="text-center">
<Link href="/" className="btn btn-secondary">
{t("common.back-homepage")}
{t('common.back-homepage')}
</Link>
</Col>
</Row>

@ -1,18 +1,18 @@
import {Col, Container, Row} from "reactstrap";
import {useTranslation} from "next-i18next";
import Link from "next/link";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import config from "../next-i18next.config.js";
import {GetStaticProps} from "next";
import { Col, Container, Row } from 'reactstrap';
import { useTranslation } from 'next-i18next';
import Link from 'next/link';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import config from '../next-i18next.config.js';
import { GetStaticProps } from 'next';
export const getStaticProps: GetStaticProps = async ({locale}) => ({
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, [], config)),
},
});
const PrivacyPolicy = (props) => {
const {t} = useTranslation();
const { t } = useTranslation();
return (
<Container>
<Row>
@ -22,7 +22,7 @@ const PrivacyPolicy = (props) => {
</Row>
<Row className="mt-4">
<Col className="text-center">
<h1>{t("Privacy policy")}</h1>
<h1>{t('Privacy policy')}</h1>
</Col>
</Row>
<Row className="mt-4">
@ -77,7 +77,7 @@ const PrivacyPolicy = (props) => {
</ul>
<p>
{" "}
{' '}
La finalité de traitement de ces données personnelles est de
permettre à lAssociation de fournir le service. Ces données sont
traitées au moment de la création du vote pour envoyer les

@ -1,28 +1,25 @@
import dynamic from 'next/dynamic';
// import Plot from 'react-plotly.js';
import React, {Component} from 'react';
import React, { Component } from 'react';
class BarChart extends Component {
render() {
return (
<div>
</div>
)
};
return <div></div>;
}
}
export default BarChart;
// <Plot
// data={[
// {
// type: 'bar',
// x: ['Taubira', 'Hidalgo', 'Mélenchon'],
// y: [29, 150, 85]
// }
// ]}
// layout={{width: 1000, height: 500, title: 'Nombre de voix par candidat'}}
// config={{
// displayModeBar: false // this is the line that hides the bar.
// }}
// />
// <Plot
// data={[
// {
// type: 'bar',
// x: ['Taubira', 'Hidalgo', 'Mélenchon'],
// y: [29, 150, 85]
// }
// ]}
// layout={{width: 1000, height: 500, title: 'Nombre de voix par candidat'}}
// config={{
// displayModeBar: false // this is the line that hides the bar.
// }}
// />

@ -1,9 +1,9 @@
import {useState} from "react";
import Head from "next/head";
import {useTranslation} from "next-i18next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import {useRouter} from "next/router";
import Link from "next/link";
import { useState } from 'react';
import Head from 'next/head';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useRouter } from 'next/router';
import Link from 'next/link';
import {
Container,
Row,
@ -13,20 +13,24 @@ import {
CardHeader,
CardBody,
Table,
Button
} from "reactstrap";
import {getResults, getDetails, apiErrors} from "@services/api";
import {grades} from "@services/grades";
import {translateGrades} from "@services/grades";
import Facebook from "@components/banner/Facebook";
import Error from "@components/Error";
import config from "../../../next-i18next.config.js";
import Footer from '@components/layouts/Footer'
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faChevronDown, faChevronRight, faChevronUp} from "@fortawesome/free-solid-svg-icons";
Button,
} from 'reactstrap';
import { getResults, getDetails, apiErrors } from '@services/api';
import { grades } from '@services/grades';
import { translateGrades } from '@services/grades';
import Facebook from '@components/banner/Facebook';
import Error from '@components/Error';
import config from '../../../next-i18next.config.js';
import Footer from '@components/layouts/Footer';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
faChevronDown,
faChevronRight,
faChevronUp,
} from '@fortawesome/free-solid-svg-icons';
export async function getServerSideProps({query, locale}) {
const {pid, tid} = query;
export async function getServerSideProps({ query, locale }) {
const { pid, tid } = query;
const [res, details, translations] = await Promise.all([
getResults(pid),
@ -34,16 +38,16 @@ export async function getServerSideProps({query, locale}) {
serverSideTranslations(locale, [], config),
]);
if (typeof res === "string" || res instanceof String) {
return {props: {err: res.slice(1, -1), ...translations}};
if (typeof res === 'string' || res instanceof String) {
return { props: { err: res.slice(1, -1), ...translations } };
}
if (typeof details === "string" || details instanceof String) {
return {props: {err: res.slice(1, -1), ...translations}};
if (typeof details === 'string' || details instanceof String) {
return { props: { err: res.slice(1, -1), ...translations } };
}
if (!details.candidates || !Array.isArray(details.candidates)) {
return {props: {err: "Unknown error", ...translations}};
return { props: { err: 'Unknown error', ...translations } };
}
return {
@ -58,13 +62,12 @@ export async function getServerSideProps({query, locale}) {
};
}
const Result = ({candidates, numGrades, title, pid, err, finish}) => {
const {t} = useTranslation();
const newstart = new Date(finish * 1000).toLocaleDateString("fr-FR");
const Result = ({ candidates, numGrades, title, pid, err, finish }) => {
const { t } = useTranslation();
const newstart = new Date(finish * 1000).toLocaleDateString('fr-FR');
if (err && err !== "") {
if (err && err !== '') {
return <Error value={apiErrors(err, t)} />;
}
@ -84,12 +87,12 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
const colSizeGradeXs = 1;
const origin =
typeof window !== "undefined" && window.location.origin
typeof window !== 'undefined' && window.location.origin
? window.location.origin
: "http://localhost";
: 'http://localhost';
const urlVote = new URL(`/vote/${pid}`, origin);
const collapsee = (candidates[0].title);
const collapsee = candidates[0].title;
const [collapseProfiles, setCollapseProfiles] = useState(false);
const [collapseGraphics, setCollapseGraphics] = useState(false);
@ -113,11 +116,14 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/calendar.svg" />
<p>{newstart}</p></Col>
<p>{newstart}</p>
</Col>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol"><img src="/avatarBlue.svg" />
<p>{" " + numVotes} votants</p></Col>
<Col className="sectionHeaderResultSideCol">
<img src="/avatarBlue.svg" />
<p>{' ' + numVotes} votants</p>
</Col>
</Row>
</Col>
@ -127,15 +133,20 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
<Col className="col-md-3 sectionHeaderResultRightCol">
<Row>
<Col className="sectionHeaderResultSideCol"><img src="/arrowUpload.svg" /><p>Télécharger les résultats</p></Col>
<Col className="sectionHeaderResultSideCol">
<img src="/arrowUpload.svg" />
<p>Télécharger les résultats</p>
</Col>
</Row>
<Row>
<Col className="sectionHeaderResultSideCol"><img src="/arrowL.svg" /><p>Partagez les résultats</p></Col>
<Col className="sectionHeaderResultSideCol">
<img src="/arrowL.svg" />
<p>Partagez les résultats</p>
</Col>
</Row>
</Col>
</Row>
<Row className="sectionHeaderResult componentMobile mx-0">
<Col className="px-0">
<h3>{title}</h3>
@ -143,9 +154,12 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
<Row>
<Col className="sectionHeaderResultSideCol">
<img src="/calendar.svg" />
<p>{newstart}</p></Col>
<Col className="sectionHeaderResultSideCol"><img src="/avatarBlue.svg" />
<p>{" " + numVotes} votants</p></Col>
<p>{newstart}</p>
</Col>
<Col className="sectionHeaderResultSideCol">
<img src="/avatarBlue.svg" />
<p>{' ' + numVotes} votants</p>
</Col>
</Row>
</Row>
@ -165,12 +179,11 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
backgroundColor: grades.slice(0).reverse()[
candidate.grade
].color,
color: "#fff",
color: '#fff',
}}
>
{allGrades.slice(0).reverse()[gradeValue].label}
</span>
</li>
);
})}
@ -181,9 +194,7 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
<Row className="mt-5">
<Col>
<h5>
<small>
{t("Détails des résultats")}
</small>
<small>{t('Détails des résultats')}</small>
</h5>
{candidates.map((candidate, i) => {
const gradeValue = candidate.grade + offsetGrade;
@ -194,14 +205,19 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
onClick={() => setCollapseGraphics(!collapseGraphics)}
>
<h4
className={
"m-0 " + (collapseGraphics ? "collapsed" : "")
}
className={'m-0 ' + (collapseGraphics ? 'collapsed' : '')}
>
<span key={i} className="d-flex panel-title justify-content-between">
<span
key={i}
className="d-flex panel-title justify-content-between"
>
<div className="d-flex">
<span className="resultPositionCard mr-2">{i + 1}</span>
<span className="candidateName">{candidate.name}</span>
<span className="resultPositionCard mr-2">
{i + 1}
</span>
<span className="candidateName">
{candidate.name}
</span>
</div>
<div>
<span
@ -210,13 +226,19 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
backgroundColor: grades.slice(0).reverse()[
candidate.grade
].color,
color: "#fff",
color: '#fff',
}}
>
{allGrades.slice(0).reverse()[gradeValue].label}
</span>
<FontAwesomeIcon icon={faChevronDown} className="ml-2 openIcon" />
<FontAwesomeIcon icon={faChevronUp} className="ml-2 closeIcon" />
<FontAwesomeIcon
icon={faChevronDown}
className="ml-2 openIcon"
/>
<FontAwesomeIcon
icon={faChevronUp}
className="ml-2 closeIcon"
/>
</div>
</span>
</h4>
@ -225,22 +247,18 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
<CardBody className="pt-5">
<Row className="column">
<Col>
{t("Preference profile")}
{t('Preference profile')}
<div>
<div
className="median"
style={{height: "40px"}}
style={{ height: '40px' }}
/>
<div style={{width: "100%"}}>
<div style={{ width: '100%' }}>
<div key={i}>
{/*candidate.label*/}
<div style={{width: "100%"}}>
<div style={{ width: '100%' }}>
{gradeIds
.slice(0)
.reverse()
@ -248,17 +266,16 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
const value = candidate.profile[id];
if (value > 0) {
let percent =
(value * 100) / numVotes + "%";
(value * 100) / numVotes + '%';
if (i === 0) {
percent = "auto";
percent = 'auto';
}
return (
<div
key={i}
style={{
width: percent,
backgroundColor:
grades[i].color,
backgroundColor: grades[i].color,
}}
>
&nbsp;
@ -268,14 +285,23 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
return null;
}
})}
</div></div></div>
</div>
</div>
</div>
</div>
</Col>
<Col><p>Graph bulles</p></Col>
<Col>
<p>Graph bulles</p>
</Col>
</Row>
<Row className="linkResult my-3">
<Link href="/" className="mx-auto">{t("Comment interpréter les résultats")}<FontAwesomeIcon icon={faChevronRight} className="ml-2 closeIcon" /></Link>
<Link href="/" className="mx-auto">
{t('Comment interpréter les résultats')}
<FontAwesomeIcon
icon={faChevronRight}
className="ml-2 closeIcon"
/>
</Link>
</Row>
</CardBody>
</Collapse>
@ -286,10 +312,16 @@ const Result = ({candidates, numGrades, title, pid, err, finish}) => {
</Row>
<div className="componentMobile mt-5">
<Row>
<Button className="cursorPointer btn-result btn-validation mb-5 btn btn-secondary"><img src="/arrowUpload.svg" /><p>Télécharger les résultats</p></Button>
<Button className="cursorPointer btn-result btn-validation mb-5 btn btn-secondary">
<img src="/arrowUpload.svg" />
<p>Télécharger les résultats</p>
</Button>
</Row>
<Row>
<Button className="cursorPointer btn-result btn-validation mb-5 btn btn-secondary"><img src="/arrowL.svg" /><p>Partagez les résultats</p></Button>
<Button className="cursorPointer btn-result btn-validation mb-5 btn btn-secondary">
<img src="/arrowL.svg" />
<p>Partagez les résultats</p>
</Button>
</Row>
</div>
</section>

@ -1,9 +1,9 @@
import {useState, useEffect} from "react";
import Head from "next/head";
import {useRouter} from "next/router";
import {useTranslation} from "next-i18next";
import {GetStaticProps} from "next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import { useState, useEffect } from 'react';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useTranslation } from 'next-i18next';
import { GetStaticProps } from 'next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import {
Collapse,
Container,
@ -13,7 +13,7 @@ import {
Button,
Card,
CardBody,
} from "reactstrap";
} from 'reactstrap';
// import {ReactMultiEmail, isEmail} from "react-multi-email";
// import "react-multi-email/style.css";
// import {toast, ToastContainer} from "react-toastify";
@ -39,14 +39,14 @@ import {
// Error messages
// const AT_LEAST_2_CANDIDATES_ERROR = "Please add at least 2 candidates.";
// const NO_TITLE_ERROR = "Please add a title.";
//
//
// const isValidDate = (date) => date instanceof Date && !isNaN(date);
// const getOnlyValidDate = (date) => (isValidDate(date) ? date : new Date());
//
//
// // Convert a Date object into YYYY-MM-DD
// const dateToISO = (date) =>
// getOnlyValidDate(date).toISOString().substring(0, 10);
//
//
// // Retrieve the current hour, minute, sec, ms, time into a timestamp
// const hours = (date) => getOnlyValidDate(date).getHours() * 3600 * 1000;
// const minutes = (date) => getOnlyValidDate(date).getMinutes() * 60 * 1000;
@ -57,14 +57,14 @@ import {
// minutes(getOnlyValidDate(date)) +
// seconds(getOnlyValidDate(date)) +
// ms(getOnlyValidDate(date));
//
//
// // Retrieve the time part from a timestamp and remove the day. Return a int.
// const timeMinusDate = (date) => time(getOnlyValidDate(date));
//
//
// // Retrieve the day and remove the time. Return a Date
// const dateMinusTime = (date) =>
// new Date(getOnlyValidDate(date).getTime() - time(getOnlyValidDate(date)));
//
//
// const displayClockOptions = () =>
// Array(24)
// .fill(1)
@ -74,7 +74,7 @@ import {
// </option>
// ));
export const getStaticProps: GetStaticProps = async ({locale}) => ({
export const getStaticProps: GetStaticProps = async ({ locale }) => ({
props: {
...(await serverSideTranslations(locale, ['resource'])),
},
@ -199,8 +199,8 @@ const CreateElection = (props) => {
const [showModal, setShowModal] = useState(false);
return (<p>FOO</p>);
}
return <p>FOO</p>;
};
// <Container className="addCandidatePage">
// <Head>
// <meta
@ -229,80 +229,80 @@ const CreateElection = (props) => {
// <Col className="stepFormCol">
// <img src="/icone-three-dark.svg" />
// <h4>Confirmation</h4>
//
//
// </Col>
// </Row>
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
// <Row className="mt-4">
// <Col xs="12">
// <CandidatesField onChange={setCandidates} />
@ -449,7 +449,7 @@ const CreateElection = (props) => {
// </select>
// </Col>
// </Row>
//
//
// <Row className="mt-2">
// <Col xs="12" md="3" lg="3">
// <span className="label">- {t("Ending date")}</span>

File diff suppressed because it is too large Load Diff

@ -1,29 +1,29 @@
import Head from "next/head";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {faChevronRight} from "@fortawesome/free-solid-svg-icons";
import {Col, Container, Row, Button} from "reactstrap";
import Link from "next/link";
import {useTranslation} from "next-i18next";
import {serverSideTranslations} from "next-i18next/serverSideTranslations";
import Head from 'next/head';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faChevronRight } from '@fortawesome/free-solid-svg-icons';
import { Col, Container, Row, Button } from 'reactstrap';
import Link from 'next/link';
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
// import PaypalNoLogo from "@components/banner/PaypalNoLogo";
import Gform from "@components/banner/Gform";
import Error from "@components/Error";
import {getDetails, apiErrors} from "@services/api";
import config from "../../../next-i18next.config.js";
import {motion} from 'framer-motion';
import Gform from '@components/banner/Gform';
import Error from '@components/Error';
import { getDetails, apiErrors } from '@services/api';
import config from '../../../next-i18next.config.js';
import { motion } from 'framer-motion';
export async function getServerSideProps({query: {pid}, locale}) {
export async function getServerSideProps({ query: { pid }, locale }) {
const [details, translations] = await Promise.all([
getDetails(pid),
serverSideTranslations(locale, [], config),
]);
if (typeof details === "string" || details instanceof String) {
return {props: {err: res.slice(1, -1), ...translations}};
if (typeof details === 'string' || details instanceof String) {
return { props: { err: res.slice(1, -1), ...translations } };
}
if (!details.candidates || !Array.isArray(details.candidates)) {
return {props: {err: "Unknown error", ...translations}};
return { props: { err: 'Unknown error', ...translations } };
}
return {
@ -31,7 +31,7 @@ export async function getServerSideProps({query: {pid}, locale}) {
...translations,
invitationOnly: details.on_invitation_only,
restrictResults: details.restrict_results,
candidates: details.candidates.map((name, i) => ({id: i, label: name})),
candidates: details.candidates.map((name, i) => ({ id: i, label: name })),
title: details.title,
numGrades: details.num_grades,
pid: pid,
@ -39,68 +39,67 @@ export async function getServerSideProps({query: {pid}, locale}) {
};
}
const VoteSuccess = ({title, invitationOnly, pid, err}) => {
const {t} = useTranslation();
if (err && err !== "") {
const VoteSuccess = ({ title, invitationOnly, pid, err }) => {
const { t } = useTranslation();
if (err && err !== '') {
return <Error value={apiErrors(err, t)} />;
}
return (
<Container className="full-height-container">
<Head>
<title>{t("resource.voteSuccess")}</title>
<title>{t('resource.voteSuccess')}</title>
<link rel="icon" href="/favicon.ico" />
<meta key="og:title" property="og:title" content={title} />
<meta
property="og:description"
key="og:description"
content={t("common.application")}
content={t('common.application')}
/>
</Head>
<motion.div
className="mx-auto"
initial={{scale: 1, paddingBottom: '200px'}}
animate={{scale: 0.5, paddingBottom: '0px'}}
initial={{ scale: 1, paddingBottom: '200px' }}
animate={{ scale: 0.5, paddingBottom: '0px' }}
transition={{
type: "spring",
type: 'spring',
damping: 100,
delay: 3
delay: 3,
}}
>
<Row>
<motion.div
className="main-animation"
initial={{opacity: 0}}
animate={{opacity: 1}}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{
type: "spring",
type: 'spring',
damping: 20,
delay: 1
delay: 1,
}}
>
<motion.div
className="vote-animation"
initial={{scale: 0, y: 50}}
animate={{scale: 1, y: 0}}
initial={{ scale: 0, y: 50 }}
animate={{ scale: 1, y: 0 }}
transition={{
type: "spring",
type: 'spring',
stiffness: 260,
damping: 20,
delay: 1
delay: 1,
}}
>
<img src="/vote.svg" />
</motion.div>
<motion.div
className="star-animation"
initial={{scale: 0}}
animate={{scale: 1}}
initial={{ scale: 0 }}
animate={{ scale: 1 }}
transition={{
type: "spring",
type: 'spring',
damping: 20,
delay: 2
delay: 2,
}}
>
<img src="/vote-star.svg" />
@ -109,74 +108,85 @@ const VoteSuccess = ({title, invitationOnly, pid, err}) => {
</Row>
</motion.div>
<motion.div
className=""
initial={{scale: 0, opacity: 0, y: 100}}
animate={{scale: 1, opacity: 1, y: -70}}
initial={{ scale: 0, opacity: 0, y: 100 }}
animate={{ scale: 1, opacity: 1, y: -70 }}
transition={{
type: "spring",
type: 'spring',
damping: 100,
delay: 4
delay: 4,
}}
>
<Row className="mt-4 px-3 confirmRowOne">
<Col className="text-center">
<h2 className="confirmH2">{t("resource.voteSuccess")}</h2>
<h2 className="confirmH2">{t('resource.voteSuccess')}</h2>
<Button className="voteDesktop mx-auto mt-4 mb-5">
{t("Voir les résultats")}
{t('Voir les résultats')}
<img src="/arrow-white.svg" className="mr-2" />
</Button>
<Button className="voteMobile mx-auto mt-4 mb-5">
{t("Voir les résultats")}
{t('Voir les résultats')}
<img src="/arrow-white.svg" className="mr-2" />
</Button>
</Col>
</Row>
<Row className="confirmRowTwo justify-content-center mb-5 px-4">
<Col className="confirmLeft">
<h2 className="confirmH2 mb-4">{t("Découvrez le jugement majoritaire")}</h2>
<p>{t("créé par des chercheurs français, le jugement majoritaire est un mode de scrutin qui améliore lexpressivité des électeurs et fournit le meilleur consensus.")}</p>
<Link href="/"><div>{t("En savoir plus")}<FontAwesomeIcon icon={faChevronRight} className="ml-2" /></div></Link>
<h2 className="confirmH2 mb-4">
{t('Découvrez le jugement majoritaire')}
</h2>
<p>
{t(
'créé par des chercheurs français, le jugement majoritaire est un mode de scrutin qui améliore lexpressivité des électeurs et fournit le meilleur consensus.'
)}
</p>
<Link href="/">
<div>
{t('En savoir plus')}
<FontAwesomeIcon icon={faChevronRight} className="ml-2" />
</div>
</Link>
</Col>
<Col className="confirmRight">
<Row className="align-items-center">
<Col xs="8" className="pr-0">
<h2 className="confirmH2">{t("Soutenez Mieux Voter")}</h2>
<h2 className="confirmH2">{t('Soutenez Mieux Voter')}</h2>
</Col>
<Col xs="4" className="text-right">
<img src="/logo-red-blue.svg" alt="logo of Mieux Voter" />
</Col>
</Row>
<p className="pt-4">{t("Mieux Voter est une association transpartisane et sans but lucratif. En adhérant à lassociation, vous contribuez à financer son fonctionnement et ses activités. ")}</p>
<p className="pt-4">
{t(
'Mieux Voter est une association transpartisane et sans but lucratif. En adhérant à lassociation, vous contribuez à financer son fonctionnement et ses activités. '
)}
</p>
</Col>
</Row>
<Row>
<Col className="text-center col-md-3 mx-auto my-5 thanksVote">
<h4>{t("resource.thanks")}</h4>
<p>{t("Aidez nous à améliorer lapplication en cliquant ci-dessous")}</p>
<h4>{t('resource.thanks')}</h4>
<p>
{t('Aidez nous à améliorer lapplication en cliquant ci-dessous')}
</p>
<Gform className="btn btn-secondary mt-3 mx-auto" />
</Col>
</Row>
<div className="mx-auto my-5">
<Row className="justify-content-center">
<Link href="https://www.facebook.com/mieuxvoter.fr/"><img src="/facebook.svg" className="mr-2" /></Link>
<p className="m-0">{t("Faites découvrir lapplication a vos amis")}</p>
<Link href="https://www.facebook.com/mieuxvoter.fr/">
<img src="/facebook.svg" className="mr-2" />
</Link>
<p className="m-0">
{t('Faites découvrir lapplication a vos amis')}
</p>
</Row>
</div>
</motion.div>
</Container >
</Container>
);
};
export default VoteSuccess;

Loading…
Cancel
Save