parent
75d1c383f5
commit
85b03f3fc0
@ -1 +0,0 @@
|
||||
|
@ -1,5 +1,13 @@
|
||||
const colors = ["#015411", "#019812", "#6bca24", "#ffb200", "#ff5d00", "#b20616", "#6f0214"];
|
||||
export const grades = process.env.REACT_APP_GRADES.split(', ').map(
|
||||
(e, i) => ({label: e, color:colors[i]}));
|
||||
|
||||
|
||||
const colors = [
|
||||
"#015411",
|
||||
"#019812",
|
||||
"#6bca24",
|
||||
"#ffb200",
|
||||
"#ff5d00",
|
||||
"#b20616",
|
||||
"#6f0214"
|
||||
];
|
||||
export const grades = process.env.REACT_APP_GRADES.split(", ").map((e, i) => ({
|
||||
label: e,
|
||||
color: colors[i]
|
||||
}));
|
||||
|
@ -1,55 +1,61 @@
|
||||
import React, {Component} from "react";
|
||||
import React, { Component } from "react";
|
||||
import ModalConfirm from "./ModalConfirm";
|
||||
|
||||
|
||||
class ButtonWithConfirm extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._modalConfirm=React.createRef();
|
||||
this.state={
|
||||
focused:false
|
||||
}
|
||||
}
|
||||
|
||||
getComponent= (key) => {
|
||||
return this.props.children.filter( (comp) => {
|
||||
return comp.key === key;
|
||||
});
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this._modalConfirm = React.createRef();
|
||||
this.state = {
|
||||
focused: false
|
||||
};
|
||||
|
||||
render() {
|
||||
const classNames=this.props.className.split(" ");
|
||||
|
||||
let classNameForDiv="";
|
||||
let classNameForButton="";
|
||||
classNames.forEach(function(className){
|
||||
if(className==="input-group-prepend" || className==="input-group-append" ){
|
||||
classNameForDiv+=" "+className;
|
||||
}else{
|
||||
classNameForButton+=" "+className;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div className={classNameForDiv}>
|
||||
<button
|
||||
type="button"
|
||||
className={classNameForButton}
|
||||
onClick={() => { this._modalConfirm.current.toggle() }}
|
||||
tabIndex={this.props.tabIndex}
|
||||
>{this.getComponent("button")}
|
||||
</button>
|
||||
<ModalConfirm className={this.props.modalClassName} ref={this._modalConfirm}>
|
||||
<div key="title">{this.getComponent("modal-title")}</div>
|
||||
<div key="body">{this.getComponent("modal-body")}</div>
|
||||
<div key="confirm">{this.getComponent("modal-confirm")}</div>
|
||||
<div key="cancel">{this.getComponent("modal-cancel")}</div>
|
||||
</ModalConfirm>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getComponent = key => {
|
||||
return this.props.children.filter(comp => {
|
||||
return comp.key === key;
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
const classNames = this.props.className.split(" ");
|
||||
|
||||
let classNameForDiv = "";
|
||||
let classNameForButton = "";
|
||||
classNames.forEach(function(className) {
|
||||
if (
|
||||
className === "input-group-prepend" ||
|
||||
className === "input-group-append"
|
||||
) {
|
||||
classNameForDiv += " " + className;
|
||||
} else {
|
||||
classNameForButton += " " + className;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classNameForDiv}>
|
||||
<button
|
||||
type="button"
|
||||
className={classNameForButton}
|
||||
onClick={() => {
|
||||
this._modalConfirm.current.toggle();
|
||||
}}
|
||||
tabIndex={this.props.tabIndex}
|
||||
>
|
||||
{this.getComponent("button")}
|
||||
</button>
|
||||
<ModalConfirm
|
||||
className={this.props.modalClassName}
|
||||
ref={this._modalConfirm}
|
||||
>
|
||||
<div key="title">{this.getComponent("modal-title")}</div>
|
||||
<div key="body">{this.getComponent("modal-body")}</div>
|
||||
<div key="confirm">{this.getComponent("modal-confirm")}</div>
|
||||
<div key="cancel">{this.getComponent("modal-cancel")}</div>
|
||||
</ModalConfirm>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ButtonWithConfirm;
|
||||
export default ButtonWithConfirm;
|
||||
|
@ -1,45 +1,76 @@
|
||||
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
|
||||
};
|
||||
}
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
showTooltip = () => {
|
||||
console.log("show");
|
||||
this.setState({
|
||||
tooltipOpen: true
|
||||
});
|
||||
this.state = {
|
||||
tooltipOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
hideTooltip = () => {
|
||||
console.log("hide");
|
||||
this.setState({
|
||||
tooltipOpen: false
|
||||
});
|
||||
};
|
||||
showTooltip = () => {
|
||||
console.log("show");
|
||||
this.setState({
|
||||
tooltipOpen: true
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<span>
|
||||
<span>
|
||||
{this.state.tooltipOpen?<span style={{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"}}>
|
||||
<span style={{ position:"absolute", width: 0,height: 0, borderTop: "10px solid transparent", borderBottom: "10px solid transparent", borderLeft: "10px solid #fff", marginLeft:"190px", marginTop:"15px"}}></span>
|
||||
{this.props.children}
|
||||
</span>:<span />}
|
||||
</span>
|
||||
<FontAwesomeIcon icon={faQuestionCircle} onMouseOver={this.showTooltip} onMouseOut={this.hideTooltip}/>
|
||||
</span>
|
||||
hideTooltip = () => {
|
||||
console.log("hide");
|
||||
this.setState({
|
||||
tooltipOpen: false
|
||||
});
|
||||
};
|
||||
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<span>
|
||||
<span>
|
||||
{this.state.tooltipOpen ? (
|
||||
<span
|
||||
style={{
|
||||
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"
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
position: "absolute",
|
||||
width: 0,
|
||||
height: 0,
|
||||
borderTop: "10px solid transparent",
|
||||
borderBottom: "10px solid transparent",
|
||||
borderLeft: "10px solid #fff",
|
||||
marginLeft: "190px",
|
||||
marginTop: "15px"
|
||||
}}
|
||||
></span>
|
||||
{this.props.children}
|
||||
</span>
|
||||
) : (
|
||||
<span />
|
||||
)}
|
||||
</span>
|
||||
<FontAwesomeIcon
|
||||
icon={faQuestionCircle}
|
||||
onMouseOver={this.showTooltip}
|
||||
onMouseOut={this.hideTooltip}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default HelpButton;
|
||||
export default HelpButton;
|
||||
|
@ -1,40 +1,52 @@
|
||||
import React, {Component} from "react";
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
|
||||
import React, { Component } from "react";
|
||||
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from "reactstrap";
|
||||
|
||||
class ModalConfirm extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
modal: false
|
||||
};
|
||||
}
|
||||
|
||||
toggle = () => {
|
||||
this.setState({
|
||||
modal: !this.state.modal
|
||||
});
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
modal: false
|
||||
};
|
||||
}
|
||||
|
||||
getComponent= (key) => {
|
||||
return this.props.children.filter( (comp) => {
|
||||
return comp.key === key;
|
||||
});
|
||||
};
|
||||
toggle = () => {
|
||||
this.setState({
|
||||
modal: !this.state.modal
|
||||
});
|
||||
};
|
||||
|
||||
getComponent = key => {
|
||||
return this.props.children.filter(comp => {
|
||||
return comp.key === key;
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Modal isOpen={this.state.modal} toggle={this.toggle} className={this.props.className+" modal-dialog-centered"} >
|
||||
<ModalHeader toggle={this.toggle}>{this.getComponent("title")}</ModalHeader>
|
||||
<ModalBody>
|
||||
{this.getComponent("body")}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button color="primary-outline" className="text-primary border-primary" onClick={this.toggle}>{this.getComponent("cancel")}</Button>
|
||||
<Button color="primary" onClick={this.toggle}>{this.getComponent("confirm")}</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Modal
|
||||
isOpen={this.state.modal}
|
||||
toggle={this.toggle}
|
||||
className={this.props.className + " modal-dialog-centered"}
|
||||
>
|
||||
<ModalHeader toggle={this.toggle}>
|
||||
{this.getComponent("title")}
|
||||
</ModalHeader>
|
||||
<ModalBody>{this.getComponent("body")}</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
color="primary-outline"
|
||||
className="text-primary border-primary"
|
||||
onClick={this.toggle}
|
||||
>
|
||||
{this.getComponent("cancel")}
|
||||
</Button>
|
||||
<Button color="primary" onClick={this.toggle}>
|
||||
{this.getComponent("confirm")}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ModalConfirm;
|
||||
export default ModalConfirm;
|
||||
|
@ -1,27 +1,23 @@
|
||||
import React, {Component} from "react";
|
||||
import {Link} from "react-router-dom";
|
||||
import React, { Component } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
class Footer extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return(
|
||||
<footer className="text-center">
|
||||
<Link to="/">Accueil</Link>
|
||||
<span className="m-2">-</span>
|
||||
<a href="https://github.com/MieuxVoter">Code source</a>
|
||||
<span className="m-2">-</span>
|
||||
<a href="https://mieuxvoter.fr/">Qui sommes nous ?</a>
|
||||
<div className="mt-2">
|
||||
MieuxVoter ©
|
||||
</div>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<footer className="text-center">
|
||||
<Link to="/">Accueil</Link>
|
||||
<span className="m-2">-</span>
|
||||
<a href="https://github.com/MieuxVoter">Code source</a>
|
||||
<span className="m-2">-</span>
|
||||
<a href="https://mieuxvoter.fr/">Qui sommes nous ?</a>
|
||||
<div className="mt-2">MieuxVoter ©</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Footer;
|
||||
export default Footer;
|
||||
|
@ -1,64 +1,60 @@
|
||||
import React, {Component} from "react";
|
||||
import {
|
||||
Collapse,
|
||||
Navbar,
|
||||
NavbarToggler,
|
||||
Nav,
|
||||
NavItem
|
||||
} from 'reactstrap'
|
||||
import {Link} from "react-router-dom";
|
||||
import React, { Component } from "react";
|
||||
import { Collapse, Navbar, NavbarToggler, Nav, NavItem } from "reactstrap";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import logo from '../../logos/logo-color.svg';
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faRocket} from "@fortawesome/free-solid-svg-icons";
|
||||
import logo from "../../logos/logo-color.svg";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faRocket } from "@fortawesome/free-solid-svg-icons";
|
||||
|
||||
class Header extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this.state = {
|
||||
isOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
this.toggle = this.toggle.bind(this);
|
||||
this.state = {
|
||||
isOpen: false
|
||||
};
|
||||
}
|
||||
toggle() {
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen
|
||||
});
|
||||
}
|
||||
|
||||
toggle() {
|
||||
this.setState({
|
||||
isOpen: !this.state.isOpen
|
||||
});
|
||||
}
|
||||
|
||||
render(){
|
||||
return (
|
||||
<header>
|
||||
<Navbar color="light" light expand="md">
|
||||
<Link to="/" className="navbar-brand">
|
||||
<div className="d-flex flex-row">
|
||||
<div className="align-self-center">
|
||||
<img src={logo} alt="logo" height="32"/>
|
||||
</div>
|
||||
<div className="align-self-center ml-2">
|
||||
<div className="logo-text">
|
||||
<h1>Plateforme de vote
|
||||
<small>Jugement Majoritaire</small>
|
||||
</h1>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<NavbarToggler onClick={this.toggle} />
|
||||
<Collapse isOpen={this.state.isOpen} navbar>
|
||||
<Nav className="ml-auto" navbar>
|
||||
<NavItem>
|
||||
<Link className="text-primary nav-link" to="/create-election/"><FontAwesomeIcon icon={faRocket} className="mr-2"/> Démarrer un vote</Link>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Collapse>
|
||||
</Navbar>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<header>
|
||||
<Navbar color="light" light expand="md">
|
||||
<Link to="/" className="navbar-brand">
|
||||
<div className="d-flex flex-row">
|
||||
<div className="align-self-center">
|
||||
<img src={logo} alt="logo" height="32" />
|
||||
</div>
|
||||
<div className="align-self-center ml-2">
|
||||
<div className="logo-text">
|
||||
<h1>
|
||||
Plateforme de vote
|
||||
<small>Jugement Majoritaire</small>
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
<NavbarToggler onClick={this.toggle} />
|
||||
<Collapse isOpen={this.state.isOpen} navbar>
|
||||
<Nav className="ml-auto" navbar>
|
||||
<NavItem>
|
||||
<Link className="text-primary nav-link" to="/create-election/">
|
||||
<FontAwesomeIcon icon={faRocket} className="mr-2" /> Démarrer
|
||||
un vote
|
||||
</Link>
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</Collapse>
|
||||
</Navbar>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Header;
|
||||
export default Header;
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,97 +1,134 @@
|
||||
import React, {Component} from "react";
|
||||
import {Button, Col, Container, Row} from "reactstrap";
|
||||
import {Link} from 'react-router-dom';
|
||||
import { faCopy, faUsers } from '@fortawesome/free-solid-svg-icons';
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import React, { Component } from "react";
|
||||
import { Button, Col, Container, Row } from "reactstrap";
|
||||
import { Link } from "react-router-dom";
|
||||
import { faCopy, faUsers } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import logoLine from "../../logos/logo-line-white.svg";
|
||||
|
||||
|
||||
class UnknownView extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
this.state = {
|
||||
urlOfVote: "https://" + window.location.hostname + "/vote/" + electionSlug,
|
||||
urlOfResult: "https://" + window.location.hostname + "/result/" + electionSlug
|
||||
};
|
||||
this.urlVoteField = React.createRef();
|
||||
this.urlResultField = React.createRef();
|
||||
}
|
||||
|
||||
handleClickOnField=(event)=>{
|
||||
event.target.focus();
|
||||
event.target.select();
|
||||
};
|
||||
|
||||
handleClickOnCopyVote=(event)=>{
|
||||
const input = this.urlVoteField.current;
|
||||
input.focus();
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
this.state = {
|
||||
urlOfVote:
|
||||
"https://" + window.location.hostname + "/vote/" + electionSlug,
|
||||
urlOfResult:
|
||||
"https://" + window.location.hostname + "/result/" + electionSlug
|
||||
};
|
||||
|
||||
handleClickOnCopyResult=(event)=>{
|
||||
const input = this.urlResultField.current;
|
||||
input.focus();
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
};
|
||||
|
||||
render(){
|
||||
return(
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4"><img src={logoLine} alt="logo" height="128" /></Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center offset-lg-3" lg="6"><h2>Vote créé avec succès !</h2>
|
||||
<p className="mt-4 mb-1">Vous pouvez maintenant partager le lien du vote aux participants :</p>
|
||||
|
||||
|
||||
<div className="input-group ">
|
||||
<input type="text" className="form-control" ref={this.urlVoteField}
|
||||
value={this.state.urlOfVote} readOnly onClick={this.handleClickOnField} />
|
||||
|
||||
<div className="input-group-append">
|
||||
<Button className="btn btn-outline-light" onClick={this.handleClickOnCopyVote}
|
||||
type="button">
|
||||
<FontAwesomeIcon icon={faCopy} className="mr-2"/>Copier</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<p className="mt-4 mb-1">Voici le lien vers les résultats du vote en temps réel :</p>
|
||||
<div className="input-group ">
|
||||
<input type="text" className="form-control" ref={this.urlResultField}
|
||||
value={this.state.urlOfResult} readOnly onClick={this.handleClickOnField} />
|
||||
|
||||
<div className="input-group-append">
|
||||
<Button className="btn btn-outline-light" onClick={this.handleClickOnCopyResult}
|
||||
type="button">
|
||||
<FontAwesomeIcon icon={faCopy} className="mr-2"/>Copier</Button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-4 mb-4" >
|
||||
<Col >
|
||||
<div className=" bg-warning text-white p-2 "><p className="m-0 p-0 text-center">Conservez ces liens précieusement !</p>
|
||||
<p className="small m-2 p-0"><b>ATTENTION</b> : Vous ne les retrouverez pas ailleurs et nous ne serons pas capable de vous les communiquer. Vous pouvez par exemple les enregistrer dans les favoris de votre
|
||||
navigateur.</p></div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="mt-4 mb-4" >
|
||||
<Col className="text-center">
|
||||
<Link to={ "/vote/" + this.props.match.params.handle} className="btn btn-success"><FontAwesomeIcon icon={faUsers} className="mr-2"/>Participer maintenant !</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
this.urlVoteField = React.createRef();
|
||||
this.urlResultField = React.createRef();
|
||||
}
|
||||
|
||||
handleClickOnField = event => {
|
||||
event.target.focus();
|
||||
event.target.select();
|
||||
};
|
||||
|
||||
handleClickOnCopyVote = event => {
|
||||
const input = this.urlVoteField.current;
|
||||
input.focus();
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
};
|
||||
|
||||
handleClickOnCopyResult = event => {
|
||||
const input = this.urlResultField.current;
|
||||
input.focus();
|
||||
input.select();
|
||||
document.execCommand("copy");
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4">
|
||||
<img src={logoLine} alt="logo" height="128" />
|
||||
</Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center offset-lg-3" lg="6">
|
||||
<h2>Vote créé avec succès !</h2>
|
||||
<p className="mt-4 mb-1">
|
||||
Vous pouvez maintenant partager le lien du vote aux participants :
|
||||
</p>
|
||||
|
||||
<div className="input-group ">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
ref={this.urlVoteField}
|
||||
value={this.state.urlOfVote}
|
||||
readOnly
|
||||
onClick={this.handleClickOnField}
|
||||
/>
|
||||
|
||||
<div className="input-group-append">
|
||||
<Button
|
||||
className="btn btn-outline-light"
|
||||
onClick={this.handleClickOnCopyVote}
|
||||
type="button"
|
||||
>
|
||||
<FontAwesomeIcon icon={faCopy} className="mr-2" />
|
||||
Copier
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="mt-4 mb-1">
|
||||
Voici le lien vers les résultats du vote en temps réel :
|
||||
</p>
|
||||
<div className="input-group ">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
ref={this.urlResultField}
|
||||
value={this.state.urlOfResult}
|
||||
readOnly
|
||||
onClick={this.handleClickOnField}
|
||||
/>
|
||||
|
||||
<div className="input-group-append">
|
||||
<Button
|
||||
className="btn btn-outline-light"
|
||||
onClick={this.handleClickOnCopyResult}
|
||||
type="button"
|
||||
>
|
||||
<FontAwesomeIcon icon={faCopy} className="mr-2" />
|
||||
Copier
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-4 mb-4">
|
||||
<Col>
|
||||
<div className=" bg-warning text-white p-2 ">
|
||||
<p className="m-0 p-0 text-center">
|
||||
Conservez ces liens précieusement !
|
||||
</p>
|
||||
<p className="small m-2 p-0">
|
||||
<b>ATTENTION</b> : Vous ne les retrouverez pas ailleurs et nous
|
||||
ne serons pas capable de vous les communiquer. Vous pouvez par
|
||||
exemple les enregistrer dans les favoris de votre navigateur.
|
||||
</p>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="mt-4 mb-4">
|
||||
<Col className="text-center">
|
||||
<Link
|
||||
to={"/vote/" + this.props.match.params.handle}
|
||||
className="btn btn-success"
|
||||
>
|
||||
<FontAwesomeIcon icon={faUsers} className="mr-2" />
|
||||
Participer maintenant !
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default UnknownView;
|
||||
|
@ -1,59 +1,90 @@
|
||||
import React, {Component} from "react";
|
||||
import { Container, Row, Col,Button, Input } from 'reactstrap';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faRocket } from '@fortawesome/free-solid-svg-icons';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import React, { Component } from "react";
|
||||
import { Container, Row, Col, Button, Input } from "reactstrap";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faRocket } from "@fortawesome/free-solid-svg-icons";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import logoLine from "../../logos/logo-line-white.svg";
|
||||
|
||||
class Home extends Component {
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
title:null,
|
||||
redirect:false,
|
||||
};
|
||||
this.focusInput= React.createRef();
|
||||
}
|
||||
|
||||
handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
this.setState({redirect: true});
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
title: null,
|
||||
redirect: false
|
||||
};
|
||||
this.focusInput = React.createRef();
|
||||
}
|
||||
|
||||
handleChangeTitle= (event) => {
|
||||
this.setState({title: event.target.value});
|
||||
};
|
||||
handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
this.setState({ redirect: true });
|
||||
};
|
||||
|
||||
render(){
|
||||
const redirect = this.state.redirect;
|
||||
handleChangeTitle = event => {
|
||||
this.setState({ title: event.target.value });
|
||||
};
|
||||
|
||||
if (redirect) {
|
||||
return <Redirect to={ '/create-election/?title='+encodeURIComponent(this.state.title)} />;
|
||||
}
|
||||
return(
|
||||
<Container>
|
||||
<form onSubmit={this.handleSubmit} autoComplete="off">
|
||||
<Row>
|
||||
<img src={logoLine} alt="logo" height="128" className="d-block ml-auto mr-auto mb-4"/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className="text-center"><h3>Simple et gratuit : organisez un vote à l'aide du Jugement Majoritaire.</h3></Col>
|
||||
</Row>
|
||||
<Row className="mt-2">
|
||||
<Col xs="12" md="9" xl="6" className="offset-xl-2">
|
||||
<Input placeholder="Saisissez ici la question de votre vote" innerRef={this.focusInput} autoFocus required className="mt-2" name="title" value={this.state.title?this.state.title:""} onChange={this.handleChangeTitle} maxLength="250" />
|
||||
</Col>
|
||||
<Col xs="12" md="3" xl="2">
|
||||
<Button type="submit" className="btn btn-block btn-secondary mt-2" ><FontAwesomeIcon icon={faRocket} className="mr-2"/>Lancer</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center"><p>Pas de publicité et pas de cookie publicitaire.</p></Col>
|
||||
</Row>
|
||||
</form>
|
||||
</Container>
|
||||
)
|
||||
};
|
||||
render() {
|
||||
const redirect = this.state.redirect;
|
||||
|
||||
if (redirect) {
|
||||
return (
|
||||
<Redirect
|
||||
to={"/create-election/?title=" + encodeURIComponent(this.state.title)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Container>
|
||||
<form onSubmit={this.handleSubmit} autoComplete="off">
|
||||
<Row>
|
||||
<img
|
||||
src={logoLine}
|
||||
alt="logo"
|
||||
height="128"
|
||||
className="d-block ml-auto mr-auto mb-4"
|
||||
/>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col className="text-center">
|
||||
<h3>
|
||||
Simple et gratuit : organisez un vote à l'aide du Jugement
|
||||
Majoritaire.
|
||||
</h3>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-2">
|
||||
<Col xs="12" md="9" xl="6" className="offset-xl-2">
|
||||
<Input
|
||||
placeholder="Saisissez ici la question de votre vote"
|
||||
innerRef={this.focusInput}
|
||||
autoFocus
|
||||
required
|
||||
className="mt-2"
|
||||
name="title"
|
||||
value={this.state.title ? this.state.title : ""}
|
||||
onChange={this.handleChangeTitle}
|
||||
maxLength="250"
|
||||
/>
|
||||
</Col>
|
||||
<Col xs="12" md="3" xl="2">
|
||||
<Button
|
||||
type="submit"
|
||||
className="btn btn-block btn-secondary mt-2"
|
||||
>
|
||||
<FontAwesomeIcon icon={faRocket} className="mr-2" />
|
||||
Lancer
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center">
|
||||
<p>Pas de publicité et pas de cookie publicitaire.</p>
|
||||
</Col>
|
||||
</Row>
|
||||
</form>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Home;
|
||||
|
@ -1,243 +1,344 @@
|
||||
import React, {Component} from "react";
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { resolve } from 'url';
|
||||
import {Container, Row, Col, Collapse, Card, CardHeader, CardBody, Table} from "reactstrap";
|
||||
import { grades } from '../../Util';
|
||||
import React, { Component } from "react";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import { resolve } from "url";
|
||||
import {
|
||||
Container,
|
||||
Row,
|
||||
Col,
|
||||
Collapse,
|
||||
Card,
|
||||
CardHeader,
|
||||
CardBody,
|
||||
Table
|
||||
} from "reactstrap";
|
||||
import { grades } from "../../Util";
|
||||
|
||||
class Result extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
candidates: [],
|
||||
title: null,
|
||||
numGrades: 0,
|
||||
colSizeCandidateLg: 4,
|
||||
colSizeCandidateMd: 6,
|
||||
colSizeCandidateXs: 12,
|
||||
colSizeGradeLg: 1,
|
||||
colSizeGradeMd: 1,
|
||||
colSizeGradeXs: 1,
|
||||
collapseGraphics: false,
|
||||
collapseProfiles: false,
|
||||
redirectLost: false,
|
||||
electionGrades: grades
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
candidates: [],
|
||||
title: null,
|
||||
numGrades: 0,
|
||||
colSizeCandidateLg: 4,
|
||||
colSizeCandidateMd: 6,
|
||||
colSizeCandidateXs: 12,
|
||||
colSizeGradeLg: 1,
|
||||
colSizeGradeMd: 1,
|
||||
colSizeGradeXs: 1,
|
||||
collapseGraphics: false,
|
||||
collapseProfiles: false,
|
||||
redirectLost: false,
|
||||
electionGrades: grades
|
||||
};
|
||||
handleErrors = response => {
|
||||
if (!response.ok) {
|
||||
response.json().then(response => {
|
||||
this.setState(state => ({
|
||||
redirectLost: "/unknown-election/" + encodeURIComponent(response)
|
||||
}));
|
||||
});
|
||||
throw Error(response);
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
handleErrors = (response) => {
|
||||
if (!response.ok) {
|
||||
response.json().then( response => {
|
||||
this.setState(state => ({
|
||||
redirectLost: '/unknown-election/' + encodeURIComponent(response)}));
|
||||
})
|
||||
throw Error(response);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
resultsToState = response => {
|
||||
const candidates = response.map(c => ({
|
||||
id: c.id,
|
||||
label: c.name,
|
||||
profile: c.profile,
|
||||
grade: c.grade,
|
||||
score: c.score
|
||||
}));
|
||||
this.setState(state => ({ candidates: candidates }));
|
||||
return response;
|
||||
};
|
||||
|
||||
resultsToState = (response) => {
|
||||
const candidates = response.map(c => ({
|
||||
id: c.id, label: c.name, profile: c.profile, grade:c.grade, score: c.score
|
||||
}));
|
||||
this.setState(state => ({candidates: candidates}));
|
||||
return response;
|
||||
}
|
||||
detailsToState = response => {
|
||||
const numGrades = response.num_grades;
|
||||
const colSizeGradeLg = Math.floor(
|
||||
(12 - this.state.colSizeCandidateLg) / numGrades
|
||||
);
|
||||
const colSizeGradeMd = Math.floor(
|
||||
(12 - this.state.colSizeCandidateMd) / numGrades
|
||||
);
|
||||
const colSizeGradeXs = Math.floor(
|
||||
(12 - this.state.colSizeCandidateXs) / numGrades
|
||||
);
|
||||
this.setState(state => ({
|
||||
title: response.title,
|
||||
numGrades: numGrades,
|
||||
colSizeGradeLg: colSizeGradeLg,
|
||||
colSizeGradeMd: colSizeGradeMd,
|
||||
colSizeGradeXs: colSizeGradeXs,
|
||||
colSizeCandidateLg:
|
||||
12 - colSizeGradeLg * numGrades > 0
|
||||
? 12 - colSizeGradeLg * numGrades
|
||||
: 12,
|
||||
colSizeCandidateMd:
|
||||
12 - colSizeGradeMd * numGrades > 0
|
||||
? 12 - colSizeGradeMd * numGrades
|
||||
: 12,
|
||||
colSizeCandidateXs:
|
||||
12 - colSizeGradeXs * numGrades > 0
|
||||
? 12 - colSizeGradeXs * numGrades
|
||||
: 12,
|
||||
electionGrades: grades.slice(0, numGrades)
|
||||
}));
|
||||
return response;
|
||||
};
|
||||
|
||||
detailsToState = (response) => {
|
||||
const numGrades = response.num_grades;
|
||||
const colSizeGradeLg = Math.floor((12 - this.state.colSizeCandidateLg) / numGrades);
|
||||
const colSizeGradeMd = Math.floor((12 - this.state.colSizeCandidateMd) / numGrades);
|
||||
const colSizeGradeXs = Math.floor((12 - this.state.colSizeCandidateXs) / numGrades);
|
||||
this.setState(state => ({
|
||||
title: response.title,
|
||||
numGrades: numGrades,
|
||||
colSizeGradeLg: colSizeGradeLg,
|
||||
colSizeGradeMd: colSizeGradeMd,
|
||||
colSizeGradeXs: colSizeGradeXs,
|
||||
colSizeCandidateLg: ((12 - colSizeGradeLg * numGrades) > 0) ? (12 - colSizeGradeLg * numGrades) : 12,
|
||||
colSizeCandidateMd: ((12 - colSizeGradeMd * numGrades) > 0) ? (12 - colSizeGradeMd * numGrades) : 12,
|
||||
colSizeCandidateXs: ((12 - colSizeGradeXs * numGrades) > 0) ? (12 - colSizeGradeXs * numGrades) : 12,
|
||||
electionGrades: grades.slice(0, numGrades)
|
||||
}));
|
||||
return response;
|
||||
}
|
||||
componentDidMount() {
|
||||
// FIXME we should better handling logs
|
||||
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
|
||||
// get details of the election
|
||||
const detailsEndpoint = resolve(
|
||||
process.env.REACT_APP_SERVER_URL,
|
||||
"election/get/".concat(electionSlug)
|
||||
);
|
||||
|
||||
componentDidMount() {
|
||||
// FIXME we should better handling logs
|
||||
fetch(detailsEndpoint)
|
||||
.then(this.handleErrors)
|
||||
.then(response => response.json())
|
||||
.then(this.detailsToState)
|
||||
.catch(error => console.log(error));
|
||||
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
// get results of the election
|
||||
const resultsEndpoint = resolve(
|
||||
process.env.REACT_APP_SERVER_URL,
|
||||
"election/results/".concat(electionSlug)
|
||||
);
|
||||
|
||||
// get details of the election
|
||||
const detailsEndpoint = resolve(process.env.REACT_APP_SERVER_URL,
|
||||
'election/get/'.concat(electionSlug));
|
||||
fetch(resultsEndpoint)
|
||||
.then(this.handleErrors)
|
||||
.then(response => response.json())
|
||||
.then(this.resultsToState)
|
||||
.catch(error => console.log(error));
|
||||
}
|
||||
|
||||
fetch(detailsEndpoint)
|
||||
.then(this.handleErrors)
|
||||
.then(response => response.json())
|
||||
.then(this.detailsToState)
|
||||
.catch(error => console.log(error));
|
||||
toggleGraphics = () => {
|
||||
this.setState(state => ({ collapseGraphics: !state.collapseGraphics }));
|
||||
};
|
||||
|
||||
// get results of the election
|
||||
const resultsEndpoint = resolve(process.env.REACT_APP_SERVER_URL,
|
||||
'election/results/'.concat(electionSlug));
|
||||
toggleProfiles = () => {
|
||||
this.setState(state => ({ collapseProfiles: !state.collapseProfiles }));
|
||||
};
|
||||
|
||||
fetch(resultsEndpoint)
|
||||
.then(this.handleErrors)
|
||||
.then(response => response.json())
|
||||
.then(this.resultsToState)
|
||||
.catch(error => console.log(error));
|
||||
render() {
|
||||
const { redirectLost, candidates, electionGrades } = this.state;
|
||||
|
||||
if (redirectLost) {
|
||||
return <Redirect to={redirectLost} />;
|
||||
}
|
||||
|
||||
toggleGraphics = () => {
|
||||
this.setState(state => ({collapseGraphics: !state.collapseGraphics}));
|
||||
};
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Col xs="12">
|
||||
<h1>{this.state.title}</h1>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
toggleProfiles = () => {
|
||||
this.setState(state => ({collapseProfiles: !state.collapseProfiles}));
|
||||
};
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<h1>Résultat du vote :</h1>
|
||||
<ol>
|
||||
{candidates.map((candidate, i) => {
|
||||
return (
|
||||
<li key={i} className="mt-2">
|
||||
{candidate.label}
|
||||
<span className="badge badge-dark mr-2 mt-2">
|
||||
{candidate.score}%
|
||||
</span>
|
||||
<span
|
||||
className="badge badge-light mr-2 mt-2"
|
||||
style={{
|
||||
backgroundColor: electionGrades[candidate.grade].color,
|
||||
color: "#fff"
|
||||
}}
|
||||
>
|
||||
{grades[candidate.grade].label}
|
||||
</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ol>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
render() {
|
||||
|
||||
const { redirectLost,
|
||||
candidates,
|
||||
electionGrades
|
||||
} = this.state;
|
||||
|
||||
if (redirectLost) {
|
||||
return (<Redirect to={redirectLost}/>)
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Col xs="12"><h1>{this.state.title}</h1></Col>
|
||||
</Row>
|
||||
|
||||
<Row className="mt-5">
|
||||
<Col><h1>Résultat du vote :</h1>
|
||||
<ol>{candidates.map((candidate, i) => {
|
||||
return (<li key={i} className="mt-2">{candidate.label}<span
|
||||
className="badge badge-dark mr-2 mt-2">{candidate.score}%</span><span
|
||||
className="badge badge-light mr-2 mt-2" style={{
|
||||
backgroundColor: electionGrades[candidate.grade].color,
|
||||
color: "#fff"
|
||||
}}>{grades[candidate.grade].label}</span></li>);
|
||||
})}</ol>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<Card className="bg-light text-primary">
|
||||
<CardHeader className="pointer" onClick={this.toggleGraphics}>
|
||||
<h4 className={"m-0 panel-title " + (this.state.collapseGraphics ? "collapsed" : "")}>Graphique</h4>
|
||||
</CardHeader>
|
||||
<Collapse isOpen={this.state.collapseGraphics}>
|
||||
<CardBody className="pt-5">
|
||||
<div>
|
||||
<div className="median"
|
||||
style={{height: (candidates.length * 28) + 30}}/>
|
||||
<table style={{width: "100%"}}><tbody>
|
||||
{candidates.map((candidate, i) => {
|
||||
return (<tr key={i}>
|
||||
<td style={{width: "30px"}}>{i + 1}</td>
|
||||
{/*candidate.label*/}
|
||||
<td>
|
||||
<table style={{width: "100%"}}>
|
||||
<tbody>
|
||||
<tr>
|
||||
{candidate.profile.map((value, i) => {
|
||||
if (value > 0) {
|
||||
let percent = value + "%";
|
||||
if (i === 0) {
|
||||
percent = "auto";
|
||||
}
|
||||
return (<td key={i} style={{
|
||||
width: percent,
|
||||
backgroundColor: this.state.electionGrades[i].color
|
||||
}}> </td>);
|
||||
}else{
|
||||
return null
|
||||
}
|
||||
|
||||
})}</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>)
|
||||
})}</tbody></table>
|
||||
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<small>
|
||||
{candidates.map((candidate, i) => {
|
||||
return (
|
||||
<span key={i}>{(i > 0) ? ", " : ""}<b>{i + 1}</b>: {candidate.label}</span>);
|
||||
})}
|
||||
</small>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<small>
|
||||
{electionGrades.map((grade, i) => {
|
||||
return (
|
||||
<span key={i} className="badge badge-light mr-2 mt-2" style={{
|
||||
backgroundColor: grade.color,
|
||||
color: "#fff"
|
||||
}}>{grade.label}</span>
|
||||
)})}
|
||||
</small>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<Col>
|
||||
<Card className="bg-light text-primary">
|
||||
<CardHeader className="pointer" onClick={this.toggleProfiles}>
|
||||
<h4 className={"m-0 panel-title " + (this.state.collapseProfiles ? "collapsed" : "")}>Profils
|
||||
de mérites</h4>
|
||||
</CardHeader>
|
||||
<Collapse isOpen={this.state.collapseProfiles}>
|
||||
<CardBody>
|
||||
<div className="table-responsive">
|
||||
<Table className="profiles">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
{electionGrades.map((grade, i) => {
|
||||
return (<th key={i}><span className="badge badge-light" style={{
|
||||
backgroundColor: grade.color,
|
||||
color: "#fff"
|
||||
}}>{grade.label} </span></th>);
|
||||
})}</tr>
|
||||
</thead>
|
||||
<tbody>{candidates.map((candidate, i) => {
|
||||
return (<tr key={i}>
|
||||
<td>{i + 1}</td>
|
||||
{/*candidate.label*/}
|
||||
{candidate.profile.map((value, i) => {
|
||||
return (<td key={i}>{value}%</td>);
|
||||
})}
|
||||
</tr>)
|
||||
})}</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
<small>{candidates.map((candidate, i) => {
|
||||
return (<span
|
||||
key={i}>{(i > 0) ? ", " : ""}<b>{i + 1}</b>: {candidate.label}</span>);
|
||||
})}</small>
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
<Row className="mt-5">
|
||||
<Col>
|
||||
<Card className="bg-light text-primary">
|
||||
<CardHeader className="pointer" onClick={this.toggleGraphics}>
|
||||
<h4
|
||||
className={
|
||||
"m-0 panel-title " +
|
||||
(this.state.collapseGraphics ? "collapsed" : "")
|
||||
}
|
||||
>
|
||||
Graphique
|
||||
</h4>
|
||||
</CardHeader>
|
||||
<Collapse isOpen={this.state.collapseGraphics}>
|
||||
<CardBody className="pt-5">
|
||||
<div>
|
||||
<div
|
||||
className="median"
|
||||
style={{ height: candidates.length * 28 + 30 }}
|
||||
/>
|
||||
<table style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
{candidates.map((candidate, i) => {
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td style={{ width: "30px" }}>{i + 1}</td>
|
||||
{/*candidate.label*/}
|
||||
<td>
|
||||
<table style={{ width: "100%" }}>
|
||||
<tbody>
|
||||
<tr>
|
||||
{candidate.profile.map((value, i) => {
|
||||
if (value > 0) {
|
||||
let percent = value + "%";
|
||||
if (i === 0) {
|
||||
percent = "auto";
|
||||
}
|
||||
return (
|
||||
<td
|
||||
key={i}
|
||||
style={{
|
||||
width: percent,
|
||||
backgroundColor: this.state
|
||||
.electionGrades[i].color
|
||||
}}
|
||||
>
|
||||
|
||||
</td>
|
||||
);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
})}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<small>
|
||||
{candidates.map((candidate, i) => {
|
||||
return (
|
||||
<span key={i}>
|
||||
{i > 0 ? ", " : ""}
|
||||
<b>{i + 1}</b>: {candidate.label}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</small>
|
||||
</div>
|
||||
<div className="mt-2">
|
||||
<small>
|
||||
{electionGrades.map((grade, i) => {
|
||||
return (
|
||||
<span
|
||||
key={i}
|
||||
className="badge badge-light mr-2 mt-2"
|
||||
style={{
|
||||
backgroundColor: grade.color,
|
||||
color: "#fff"
|
||||
}}
|
||||
>
|
||||
{grade.label}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</small>
|
||||
</div>
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-3">
|
||||
<Col>
|
||||
<Card className="bg-light text-primary">
|
||||
<CardHeader className="pointer" onClick={this.toggleProfiles}>
|
||||
<h4
|
||||
className={
|
||||
"m-0 panel-title " +
|
||||
(this.state.collapseProfiles ? "collapsed" : "")
|
||||
}
|
||||
>
|
||||
Profils de mérites
|
||||
</h4>
|
||||
</CardHeader>
|
||||
<Collapse isOpen={this.state.collapseProfiles}>
|
||||
<CardBody>
|
||||
<div className="table-responsive">
|
||||
<Table className="profiles">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
{electionGrades.map((grade, i) => {
|
||||
return (
|
||||
<th key={i}>
|
||||
<span
|
||||
className="badge badge-light"
|
||||
style={{
|
||||
backgroundColor: grade.color,
|
||||
color: "#fff"
|
||||
}}
|
||||
>
|
||||
{grade.label}{" "}
|
||||
</span>
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{candidates.map((candidate, i) => {
|
||||
return (
|
||||
<tr key={i}>
|
||||
<td>{i + 1}</td>
|
||||
{/*candidate.label*/}
|
||||
{candidate.profile.map((value, i) => {
|
||||
return <td key={i}>{value}%</td>;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</Table>
|
||||
</div>
|
||||
<small>
|
||||
{candidates.map((candidate, i) => {
|
||||
return (
|
||||
<span key={i}>
|
||||
{i > 0 ? ", " : ""}
|
||||
<b>{i + 1}</b>: {candidate.label}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</small>
|
||||
</CardBody>
|
||||
</Collapse>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Result;
|
||||
|
@ -1,34 +1,37 @@
|
||||
import React, {Component} from "react";
|
||||
import {Col, Container, Row} from "reactstrap";
|
||||
import React, { Component } from "react";
|
||||
import { Col, Container, Row } from "reactstrap";
|
||||
import logoLine from "../../logos/logo-line-white.svg";
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
class UnknownElection extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return(
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4"><img src={logoLine} alt="logo" height="128" /></Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center"><h2>Oups ! Ce vote n'existe pas ou n'est plus disponible.</h2>
|
||||
<p>N'hésitez pas à démarrer un nouveau vote</p></Col>
|
||||
</Row>
|
||||
<Row className="mt-4" >
|
||||
<Col className="text-center">
|
||||
<Link to="/" className="btn btn-secondary">Revenir à l'accueil</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4">
|
||||
<img src={logoLine} alt="logo" height="128" />
|
||||
</Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center">
|
||||
<h2>Oups ! Ce vote n'existe pas ou n'est plus disponible.</h2>
|
||||
<p>N'hésitez pas à démarrer un nouveau vote</p>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center">
|
||||
<Link to="/" className="btn btn-secondary">
|
||||
Revenir à l'accueil
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default UnknownElection;
|
||||
|
@ -1,33 +1,36 @@
|
||||
import React, {Component} from "react";
|
||||
import {Col, Container, Row} from "reactstrap";
|
||||
import React, { Component } from "react";
|
||||
import { Col, Container, Row } from "reactstrap";
|
||||
import logoLine from "../../logos/logo-line-white.svg";
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
class UnknownView extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
}
|
||||
}
|
||||
|
||||
render(){
|
||||
return(
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4"><img src={logoLine} alt="logo" height="128" /></Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center"><h2>Oups ! Cette page n'existe pas</h2></Col>
|
||||
</Row>
|
||||
<Row className="mt-4" >
|
||||
<Col className="text-center">
|
||||
<Link to="/" className="btn btn-secondary">Revenir à l'accueil</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4">
|
||||
<img src={logoLine} alt="logo" height="128" />
|
||||
</Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center">
|
||||
<h2>Oups ! Cette page n'existe pas</h2>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center">
|
||||
<Link to="/" className="btn btn-secondary">
|
||||
Revenir à l'accueil
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default UnknownView;
|
||||
|
@ -1,213 +1,318 @@
|
||||
import React, {Component} from "react";
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import {Button, Col, Container, Row} from "reactstrap";
|
||||
import {toast, ToastContainer} from "react-toastify";
|
||||
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
|
||||
import {faCheck} from "@fortawesome/free-solid-svg-icons";
|
||||
import { resolve } from 'url';
|
||||
import { grades } from '../../Util';
|
||||
|
||||
import React, { Component } from "react";
|
||||
import { Redirect } from "react-router-dom";
|
||||
import { Button, Col, Container, Row } from "reactstrap";
|
||||
import { toast, ToastContainer } from "react-toastify";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCheck } from "@fortawesome/free-solid-svg-icons";
|
||||
import { resolve } from "url";
|
||||
import { grades } from "../../Util";
|
||||
|
||||
class Vote extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
candidates: [],
|
||||
title: null,
|
||||
numGrades: 0,
|
||||
ratedCandidates: [],
|
||||
colSizeCandidateLg: 4,
|
||||
colSizeCandidateMd: 6,
|
||||
colSizeCandidateXs: 12,
|
||||
colSizeGradeLg: 1,
|
||||
colSizeGradeMd: 1,
|
||||
colSizeGradeXs: 1,
|
||||
redirectTo: null,
|
||||
electionGrades: grades
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
candidates:[],
|
||||
title:null,
|
||||
numGrades:0,
|
||||
ratedCandidates:[],
|
||||
colSizeCandidateLg:4,
|
||||
colSizeCandidateMd:6,
|
||||
colSizeCandidateXs:12,
|
||||
colSizeGradeLg:1,
|
||||
colSizeGradeMd:1,
|
||||
colSizeGradeXs:1,
|
||||
redirectTo: null,
|
||||
electionGrades: grades
|
||||
};
|
||||
|
||||
handleErrors = response => {
|
||||
if (!response.ok) {
|
||||
response.json().then(response => {
|
||||
console.log(response);
|
||||
this.setState(state => ({
|
||||
redirectTo: "/unknown-election/" + encodeURIComponent(response)
|
||||
}));
|
||||
});
|
||||
throw Error(response);
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
handleErrors = (response) => {
|
||||
if (!response.ok) {
|
||||
response.json().then( response => {
|
||||
console.log(response);
|
||||
this.setState(state => ({
|
||||
redirectTo: '/unknown-election/' + encodeURIComponent(response)}));
|
||||
})
|
||||
throw Error(response);
|
||||
}
|
||||
return response;
|
||||
detailsToState = response => {
|
||||
const numGrades = response.num_grades;
|
||||
const candidates = response.candidates.map((c, i) => ({
|
||||
id: i,
|
||||
label: c
|
||||
}));
|
||||
//shuffle candidates
|
||||
let i, j, temp;
|
||||
for (i = candidates.length - 1; i > 0; i--) {
|
||||
j = Math.floor(Math.random() * (i + 1));
|
||||
temp = candidates[i];
|
||||
candidates[i] = candidates[j];
|
||||
candidates[j] = temp;
|
||||
}
|
||||
const colSizeGradeLg = Math.floor(
|
||||
(12 - this.state.colSizeCandidateLg) / numGrades
|
||||
);
|
||||
const colSizeGradeMd = Math.floor(
|
||||
(12 - this.state.colSizeCandidateMd) / numGrades
|
||||
);
|
||||
const colSizeGradeXs = Math.floor(
|
||||
(12 - this.state.colSizeCandidateXs) / numGrades
|
||||
);
|
||||
|
||||
detailsToState = (response) => {
|
||||
const numGrades = response.num_grades;
|
||||
const candidates = response.candidates.map((c, i) => ({
|
||||
id: i,
|
||||
label: c
|
||||
}));
|
||||
//shuffle candidates
|
||||
let i, j, temp;
|
||||
for (i = candidates.length - 1; i > 0; i--) {
|
||||
j = Math.floor(Math.random() * (i + 1));
|
||||
temp = candidates[i];
|
||||
candidates[i] = candidates[j];
|
||||
candidates[j] = temp;
|
||||
}
|
||||
const colSizeGradeLg = Math.floor((12 - this.state.colSizeCandidateLg) / numGrades);
|
||||
const colSizeGradeMd = Math.floor((12 - this.state.colSizeCandidateMd) / numGrades);
|
||||
const colSizeGradeXs = Math.floor((12 - this.state.colSizeCandidateXs) / numGrades);
|
||||
this.setState(state => ({
|
||||
title: response.title,
|
||||
candidates: candidates,
|
||||
numGrades: numGrades,
|
||||
colSizeGradeLg: colSizeGradeLg,
|
||||
colSizeGradeMd: colSizeGradeMd,
|
||||
colSizeGradeXs: colSizeGradeXs,
|
||||
colSizeCandidateLg:
|
||||
12 - colSizeGradeLg * numGrades > 0
|
||||
? 12 - colSizeGradeLg * numGrades
|
||||
: 12,
|
||||
colSizeCandidateMd:
|
||||
12 - colSizeGradeMd * numGrades > 0
|
||||
? 12 - colSizeGradeMd * numGrades
|
||||
: 12,
|
||||
colSizeCandidateXs:
|
||||
12 - colSizeGradeXs * numGrades > 0
|
||||
? 12 - colSizeGradeXs * numGrades
|
||||
: 12,
|
||||
electionGrades: grades.slice(0, numGrades)
|
||||
}));
|
||||
return response;
|
||||
};
|
||||
|
||||
this.setState(state => ({
|
||||
title: response.title,
|
||||
candidates: candidates,
|
||||
numGrades: numGrades,
|
||||
colSizeGradeLg: colSizeGradeLg,
|
||||
colSizeGradeMd: colSizeGradeMd,
|
||||
colSizeGradeXs: colSizeGradeXs,
|
||||
colSizeCandidateLg: ((12 - colSizeGradeLg * numGrades) > 0) ?
|
||||
(12 - colSizeGradeLg * numGrades) : 12,
|
||||
colSizeCandidateMd: ((12 - colSizeGradeMd * numGrades) > 0) ?
|
||||
(12 - colSizeGradeMd * numGrades) : 12,
|
||||
colSizeCandidateXs: ((12 - colSizeGradeXs * numGrades) > 0) ?
|
||||
(12 - colSizeGradeXs * numGrades) : 12,
|
||||
electionGrades: grades.slice(0, numGrades)
|
||||
}));
|
||||
return response;
|
||||
}
|
||||
componentDidMount() {
|
||||
// FIXME we should better handling logs
|
||||
|
||||
componentDidMount() {
|
||||
// FIXME we should better handling logs
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
const detailsEndpoint = resolve(
|
||||
process.env.REACT_APP_SERVER_URL,
|
||||
"election/get/".concat(electionSlug)
|
||||
);
|
||||
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
const detailsEndpoint = resolve(process.env.REACT_APP_SERVER_URL,
|
||||
'election/get/'.concat(electionSlug));
|
||||
fetch(detailsEndpoint)
|
||||
.then(this.handleErrors)
|
||||
.then(response => response.json())
|
||||
.then(this.detailsToState)
|
||||
.catch(error => console.log(error));
|
||||
}
|
||||
|
||||
fetch(detailsEndpoint)
|
||||
.then(this.handleErrors)
|
||||
.then(response => response.json())
|
||||
.then(this.detailsToState)
|
||||
.catch(error => console.log(error));
|
||||
}
|
||||
handleGradeClick = event => {
|
||||
let data = {
|
||||
id: parseInt(event.currentTarget.getAttribute("data-id")),
|
||||
value: parseInt(event.currentTarget.value)
|
||||
};
|
||||
//remove candidate
|
||||
let ratedCandidates = this.state.ratedCandidates.filter(
|
||||
ratedCandidate => ratedCandidate.id !== data.id
|
||||
);
|
||||
ratedCandidates.push(data);
|
||||
this.setState({ ratedCandidates: ratedCandidates });
|
||||
};
|
||||
|
||||
handleSubmitWithoutAllRate = () => {
|
||||
toast.error("Vous devez évaluer l'ensemble des propositions/candidats !", {
|
||||
position: toast.POSITION.TOP_CENTER
|
||||
});
|
||||
};
|
||||
|
||||
handleGradeClick = (event) => {
|
||||
let data={
|
||||
id:parseInt(event.currentTarget.getAttribute("data-id")),
|
||||
value:parseInt(event.currentTarget.value)
|
||||
};
|
||||
//remove candidate
|
||||
let ratedCandidates = this.state.ratedCandidates.filter(ratedCandidate => ratedCandidate.id !== data.id);
|
||||
ratedCandidates.push(data);
|
||||
this.setState({ratedCandidates:ratedCandidates});
|
||||
handleSubmit = event => {
|
||||
event.preventDefault();
|
||||
|
||||
};
|
||||
const { ratedCandidates } = this.state;
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
const endpoint = resolve(
|
||||
process.env.REACT_APP_SERVER_URL,
|
||||
"election/vote/"
|
||||
);
|
||||
|
||||
handleSubmitWithoutAllRate = () => {
|
||||
toast.error("Vous devez évaluer l'ensemble des propositions/candidats !", {
|
||||
position: toast.POSITION.TOP_CENTER
|
||||
});
|
||||
};
|
||||
const gradesById = {};
|
||||
ratedCandidates.forEach(c => {
|
||||
gradesById[c.id] = c.value;
|
||||
});
|
||||
const gradesByCandidate = [];
|
||||
Object.keys(gradesById)
|
||||
.sort()
|
||||
.forEach(id => {
|
||||
gradesByCandidate.push(gradesById[id]);
|
||||
});
|
||||
|
||||
handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
const { ratedCandidates } = this.state;
|
||||
const electionSlug = this.props.match.params.handle;
|
||||
const endpoint = resolve(process.env.REACT_APP_SERVER_URL,
|
||||
'election/vote/');
|
||||
|
||||
const gradesById = {};
|
||||
ratedCandidates.forEach(c => { gradesById[c.id] = c.value; });
|
||||
const gradesByCandidate = [];
|
||||
Object.keys(gradesById)
|
||||
.sort()
|
||||
.forEach(id => {gradesByCandidate.push(gradesById[id]);});
|
||||
|
||||
fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
election: electionSlug,
|
||||
grades_by_candidate: gradesByCandidate,
|
||||
})
|
||||
})
|
||||
.then(this.handleErrors)
|
||||
.then(result => this.setState({redirectTo: '/vote-success/' + electionSlug}))
|
||||
.catch(error => error);
|
||||
};
|
||||
fetch(endpoint, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
election: electionSlug,
|
||||
grades_by_candidate: gradesByCandidate
|
||||
})
|
||||
})
|
||||
.then(this.handleErrors)
|
||||
.then(result =>
|
||||
this.setState({ redirectTo: "/vote-success/" + electionSlug })
|
||||
)
|
||||
.catch(error => error);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { redirectTo, candidates, electionGrades } = this.state;
|
||||
|
||||
render(){
|
||||
|
||||
const { redirectTo,
|
||||
candidates,
|
||||
electionGrades
|
||||
} = this.state;
|
||||
|
||||
if (redirectTo) {
|
||||
return (<Redirect to={redirectTo}/>);
|
||||
}
|
||||
|
||||
return(
|
||||
<Container>
|
||||
<ToastContainer/>
|
||||
<form onSubmit={this.handleSubmit} autoComplete="off" >
|
||||
<Row>
|
||||
<Col ><h3>{ this.state.title }</h3></Col>
|
||||
</Row>
|
||||
<Row className="cardVote d-none d-lg-flex" >
|
||||
<Col xs={this.state.colSizeCandidateXs} md={this.state.colSizeCandidateMd} lg={this.state.colSizeCandidateLg} ><h5 > </h5></Col>
|
||||
{ electionGrades.map((grade,j) => {
|
||||
return (j<this.state.numGrades)?<Col xs={this.state.colSizeGradeXs} md={this.state.colSizeGradeMd} lg={this.state.colSizeGradeLg} key={j} className="text-center p-0" style={{lineHeight:2}}><small className="nowrap bold badge" style={{backgroundColor:grade.color,color:"#fff"}}>{grade.label}</small></Col>:null;
|
||||
})
|
||||
}
|
||||
</Row>
|
||||
|
||||
{
|
||||
candidates.map((candidate,i) => {
|
||||
return <Row key={i} className="cardVote">
|
||||
<Col xs={this.state.colSizeCandidateXs} md={this.state.colSizeCandidateMd} lg={this.state.colSizeCandidateLg} >
|
||||
<h5 className="m-0">{candidate.label}</h5><hr className="d-lg-none" /></Col>
|
||||
{ this.state.electionGrades.map((grade,j) => {
|
||||
return (j<this.state.numGrades)?<Col
|
||||
xs={this.state.colSizeGradeXs} md={this.state.colSizeGradeMd} lg={this.state.colSizeGradeLg} key={j}
|
||||
className="text-lg-center"
|
||||
>
|
||||
|
||||
<label htmlFor={"candidateGrade"+i+"-"+j} className="check"
|
||||
|
||||
>
|
||||
<small className="nowrap d-lg-none ml-2 bold badge"
|
||||
style={
|
||||
this.state.ratedCandidates.find(function(ratedCandidat){return JSON.stringify(ratedCandidat) === JSON.stringify({id:candidate.id,value:j})})?
|
||||
{backgroundColor:grade.color,color:"#fff"}:{backgroundColor:'transparent',color:"#000"}
|
||||
}
|
||||
|
||||
>{grade.label}</small>
|
||||
<input type="radio" name={"candidate"+i} id={"candidateGrade"+i+"-"+j} data-index={i} data-id={candidate.id} value={j} onClick={this.handleGradeClick} defaultChecked={this.state.ratedCandidates.find(function(element) { return JSON.stringify(element) === JSON.stringify({id:candidate.id,value:j})})} />
|
||||
<span className="checkmark" style={
|
||||
this.state.ratedCandidates.find(function(ratedCandidat){return JSON.stringify(ratedCandidat) === JSON.stringify({id:candidate.id,value:j})})?
|
||||
{backgroundColor:grade.color,color:"#fff"}:{backgroundColor:'transparent',color:"#000"}
|
||||
}/>
|
||||
</label></Col>:null
|
||||
})
|
||||
}
|
||||
</Row>
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
<Row>
|
||||
<Col className="text-center" >
|
||||
{(this.state.ratedCandidates.length!==this.state.candidates.length)?<Button type="button" onClick={this.handleSubmitWithoutAllRate} className="btn btn-dark "><FontAwesomeIcon icon={faCheck} className="mr-2" />Valider</Button>:<Button type="submit" className="btn btn-success "><FontAwesomeIcon icon={faCheck} className="mr-2" />Valider</Button>}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
</form>
|
||||
</Container>
|
||||
)
|
||||
if (redirectTo) {
|
||||
return <Redirect to={redirectTo} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<ToastContainer />
|
||||
<form onSubmit={this.handleSubmit} autoComplete="off">
|
||||
<Row>
|
||||
<Col>
|
||||
<h3>{this.state.title}</h3>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row className="cardVote d-none d-lg-flex">
|
||||
<Col
|
||||
xs={this.state.colSizeCandidateXs}
|
||||
md={this.state.colSizeCandidateMd}
|
||||
lg={this.state.colSizeCandidateLg}
|
||||
>
|
||||
<h5> </h5>
|
||||
</Col>
|
||||
{electionGrades.map((grade, j) => {
|
||||
return j < this.state.numGrades ? (
|
||||
<Col
|
||||
xs={this.state.colSizeGradeXs}
|
||||
md={this.state.colSizeGradeMd}
|
||||
lg={this.state.colSizeGradeLg}
|
||||
key={j}
|
||||
className="text-center p-0"
|
||||
style={{ lineHeight: 2 }}
|
||||
>
|
||||
<small
|
||||
className="nowrap bold badge"
|
||||
style={{ backgroundColor: grade.color, color: "#fff" }}
|
||||
>
|
||||
{grade.label}
|
||||
</small>
|
||||
</Col>
|
||||
) : null;
|
||||
})}
|
||||
</Row>
|
||||
|
||||
{candidates.map((candidate, i) => {
|
||||
return (
|
||||
<Row key={i} className="cardVote">
|
||||
<Col
|
||||
xs={this.state.colSizeCandidateXs}
|
||||
md={this.state.colSizeCandidateMd}
|
||||
lg={this.state.colSizeCandidateLg}
|
||||
>
|
||||
<h5 className="m-0">{candidate.label}</h5>
|
||||
<hr className="d-lg-none" />
|
||||
</Col>
|
||||
{this.state.electionGrades.map((grade, j) => {
|
||||
return j < this.state.numGrades ? (
|
||||
<Col
|
||||
xs={this.state.colSizeGradeXs}
|
||||
md={this.state.colSizeGradeMd}
|
||||
lg={this.state.colSizeGradeLg}
|
||||
key={j}
|
||||
className="text-lg-center"
|
||||
>
|
||||
<label
|
||||
htmlFor={"candidateGrade" + i + "-" + j}
|
||||
className="check"
|
||||
>
|
||||
<small
|
||||
className="nowrap d-lg-none ml-2 bold badge"
|
||||
style={
|
||||
this.state.ratedCandidates.find(function(
|
||||
ratedCandidat
|
||||
) {
|
||||
return (
|
||||
JSON.stringify(ratedCandidat) ===
|
||||
JSON.stringify({ id: candidate.id, value: j })
|
||||
);
|
||||
})
|
||||
? { backgroundColor: grade.color, color: "#fff" }
|
||||
: {
|
||||
backgroundColor: "transparent",
|
||||
color: "#000"
|
||||
}
|
||||
}
|
||||
>
|
||||
{grade.label}
|
||||
</small>
|
||||
<input
|
||||
type="radio"
|
||||
name={"candidate" + i}
|
||||
id={"candidateGrade" + i + "-" + j}
|
||||
data-index={i}
|
||||
data-id={candidate.id}
|
||||
value={j}
|
||||
onClick={this.handleGradeClick}
|
||||
defaultChecked={this.state.ratedCandidates.find(
|
||||
function(element) {
|
||||
return (
|
||||
JSON.stringify(element) ===
|
||||
JSON.stringify({ id: candidate.id, value: j })
|
||||
);
|
||||
}
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className="checkmark"
|
||||
style={
|
||||
this.state.ratedCandidates.find(function(
|
||||
ratedCandidat
|
||||
) {
|
||||
return (
|
||||
JSON.stringify(ratedCandidat) ===
|
||||
JSON.stringify({ id: candidate.id, value: j })
|
||||
);
|
||||
})
|
||||
? { backgroundColor: grade.color, color: "#fff" }
|
||||
: {
|
||||
backgroundColor: "transparent",
|
||||
color: "#000"
|
||||
}
|
||||
}
|
||||
/>
|
||||
</label>
|
||||
</Col>
|
||||
) : null;
|
||||
})}
|
||||
</Row>
|
||||
);
|
||||
})}
|
||||
|
||||
<Row>
|
||||
<Col className="text-center">
|
||||
{this.state.ratedCandidates.length !==
|
||||
this.state.candidates.length ? (
|
||||
<Button
|
||||
type="button"
|
||||
onClick={this.handleSubmitWithoutAllRate}
|
||||
className="btn btn-dark "
|
||||
>
|
||||
<FontAwesomeIcon icon={faCheck} className="mr-2" />
|
||||
Valider
|
||||
</Button>
|
||||
) : (
|
||||
<Button type="submit" className="btn btn-success ">
|
||||
<FontAwesomeIcon icon={faCheck} className="mr-2" />
|
||||
Valider
|
||||
</Button>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
</form>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default Vote;
|
||||
|
@ -1,34 +1,33 @@
|
||||
import React, {Component} from "react";
|
||||
import {Col, Container, Row} from "reactstrap";
|
||||
import React, { Component } from "react";
|
||||
import { Col, Container, Row } from "reactstrap";
|
||||
import logoLine from "../../logos/logo-line-white.svg";
|
||||
import {Link} from 'react-router-dom';
|
||||
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
class UnknownView extends Component {
|
||||
render() {
|
||||
return (
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4">
|
||||
<img src={logoLine} alt="logo" height="128" />
|
||||
</Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center offset-lg-3" lg="6">
|
||||
<h2>Participation enregistrée avec succès !</h2>
|
||||
<p>Merci pour votre participation.</p>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
render(){
|
||||
return(
|
||||
<Container>
|
||||
<Row>
|
||||
<Link to="/" className="d-block ml-auto mr-auto mb-4">
|
||||
<img src={logoLine} alt="logo" height="128" />
|
||||
</Link>
|
||||
</Row>
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center offset-lg-3" lg="6">
|
||||
<h2>Participation enregistrée avec succès !</h2>
|
||||
<p>Merci pour votre participation.</p>
|
||||
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row className="mt-4" >
|
||||
<Col className="text-center">
|
||||
<Link to="/" className="btn btn-secondary">Revenir à l'accueil</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
<Row className="mt-4">
|
||||
<Col className="text-center">
|
||||
<Link to="/" className="btn btn-secondary">
|
||||
Revenir à l'accueil
|
||||
</Link>
|
||||
</Col>
|
||||
</Row>
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
}
|
||||
export default UnknownView;
|
||||
|
@ -1,4 +1 @@
|
||||
@import '~bootstrap/scss/bootstrap.scss';
|
||||
|
||||
|
||||
|
||||
@import "~bootstrap/scss/bootstrap.scss";
|
||||
|
@ -1,30 +1,24 @@
|
||||
// mieux voter vars
|
||||
$mv-blue-color: #2A43A0;
|
||||
$mv-red-color: #EE455B;
|
||||
$mv-blue-color: #2a43a0;
|
||||
$mv-red-color: #ee455b;
|
||||
$mv-light-color: #efefff;
|
||||
$mv-light-color-hover:rgba(#efefff, .8);
|
||||
$mv-light-color-hover: rgba(#efefff, 0.8);
|
||||
$mv-dark-color: #333;
|
||||
|
||||
// Override default variables before the import bootstrap
|
||||
$body-bg: #000000;
|
||||
$body-color: $mv-light-color;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
$theme-colors: (
|
||||
"primary": $mv-blue-color,
|
||||
"secondary": $mv-red-color,
|
||||
"light": $mv-light-color,
|
||||
"dark": $mv-dark-color,
|
||||
"danger": #990000,
|
||||
"success": #009900,
|
||||
"info": #2B8299,
|
||||
"warning": #FF6E11,
|
||||
|
||||
"primary": $mv-blue-color,
|
||||
"secondary": $mv-red-color,
|
||||
"light": $mv-light-color,
|
||||
"dark": $mv-dark-color,
|
||||
"danger": #990000,
|
||||
"success": #009900,
|
||||
"info": #2b8299,
|
||||
"warning": #ff6e11
|
||||
);
|
||||
|
||||
@import '_bootstrap.scss';
|
||||
@import 'app.scss';
|
||||
@import "_bootstrap.scss";
|
||||
@import "app.scss";
|
||||
|
Loading…
Reference in new issue