From 85b03f3fc0ceb107662a9a102e6c775d5dda9f04 Mon Sep 17 00:00:00 2001 From: Pierre-Louis Guhur Date: Fri, 27 Dec 2019 17:29:24 +0000 Subject: [PATCH] reformat code --- src/App.css | 1 - src/App.jsx | 18 +- src/App.test.jsx | 148 +-- src/Routes.jsx | 29 +- src/Util.jsx | 18 +- src/components/form/ButtonWithConfirm.jsx | 104 +- src/components/form/HelpButton.jsx | 105 +- src/components/form/ModalConfirm.jsx | 78 +- src/components/layouts/Footer.jsx | 42 +- src/components/layouts/Header.jsx | 110 +- src/components/views/CreateElection.jsx | 1102 ++++++++++++--------- src/components/views/CreateSuccess.js | 219 ++-- src/components/views/Home.jsx | 131 ++- src/components/views/Result.jsx | 545 +++++----- src/components/views/UnknownElection.js | 59 +- src/components/views/UnknownView.jsx | 57 +- src/components/views/Vote.jsx | 489 +++++---- src/components/views/VoteSuccess.js | 55 +- src/index.jsx | 13 +- src/scss/_app.scss | 216 ++-- src/scss/_bootstrap.scss | 5 +- src/scss/config.scss | 32 +- src/serviceWorker.jsx | 30 +- 23 files changed, 2057 insertions(+), 1549 deletions(-) diff --git a/src/App.css b/src/App.css index 8b13789..e69de29 100644 --- a/src/App.css +++ b/src/App.css @@ -1 +0,0 @@ - diff --git a/src/App.jsx b/src/App.jsx index 580ce64..c6253ea 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { BrowserRouter as Router} from "react-router-dom"; +import React from "react"; +import { BrowserRouter as Router } from "react-router-dom"; import Routes from "./Routes"; import Header from "./components/layouts/Header"; @@ -7,13 +7,13 @@ import Footer from "./components/layouts/Footer"; function App() { return ( - -
-
- -
-
-
+ +
+
+ +
+
+
); } diff --git a/src/App.test.jsx b/src/App.test.jsx index d931f2d..93334a0 100644 --- a/src/App.test.jsx +++ b/src/App.test.jsx @@ -1,11 +1,11 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import {MemoryRouter} from 'react-router-dom'; +import React from "react"; +import ReactDOM from "react-dom"; +import { MemoryRouter } from "react-router-dom"; -import Routes from './Routes'; -import App from './App'; -import Adapter from 'enzyme-adapter-react-16'; -import {mount, configure} from 'enzyme'; +import Routes from "./Routes"; +import App from "./App"; +import Adapter from "enzyme-adapter-react-16"; +import { mount, configure } from "enzyme"; import Home from "./components/views/Home.js"; import CreateElection from "./components/views/CreateElection.js"; @@ -13,72 +13,76 @@ import Result from "./components/views/Result.js"; import Vote from "./components/views/Vote.js"; import UnknownView from "./components/views/UnknownView"; -configure({adapter: new Adapter()}); +configure({ adapter: new Adapter() }); - -it('renders without crashing', () => { - const div = document.createElement('div'); - ReactDOM.render(, div); - ReactDOM.unmountComponentAtNode(div); +it("renders without crashing", () => { + const div = document.createElement("div"); + ReactDOM.render(, div); + ReactDOM.unmountComponentAtNode(div); }); - -describe('open good View component for each route', () => { - - it('should show Home view component for `/`', () => { - const wrapper = mount( - - - ); - expect(wrapper.find(Home)).toHaveLength(1); - expect(wrapper.find(UnknownView)).toHaveLength(0); - }); - - - it('should show CreateElection view component for `/create-election`', () => { - const wrapper = mount( - - - ); - expect(wrapper.find(CreateElection)).toHaveLength(1); - expect(wrapper.find(UnknownView)).toHaveLength(0); - }); - - it('should show CreateElection view component with title for `/create-election/?title=test%20with%20title`', () => { - const wrapper = mount( - - - ); - expect(wrapper.find(CreateElection)).toHaveLength(1); - expect(wrapper.find('input[name="title"]').props().defaultValue).toBe("test with title"); - expect(wrapper.find(UnknownView)).toHaveLength(0); - }); - - it('should show Vote view component for `/vote`', () => { - const wrapper = mount( - - - ); - expect(wrapper.find(Vote)).toHaveLength(1); - expect(wrapper.find(UnknownView)).toHaveLength(0); - }); - - it('should show Result view component for `/result`', () => { - const wrapper = mount( - - - ); - expect(wrapper.find(Result)).toHaveLength(1); - expect(wrapper.find(UnknownView)).toHaveLength(0); - }); - - - it('should show UnknownView view component for `/aaabbbcccddd`', () => { - const wrapper = mount( - - - ); - expect(wrapper.find(UnknownView)).toHaveLength(1); - }); - +describe("open good View component for each route", () => { + it("should show Home view component for `/`", () => { + const wrapper = mount( + + + + ); + expect(wrapper.find(Home)).toHaveLength(1); + expect(wrapper.find(UnknownView)).toHaveLength(0); + }); + + it("should show CreateElection view component for `/create-election`", () => { + const wrapper = mount( + + + + ); + expect(wrapper.find(CreateElection)).toHaveLength(1); + expect(wrapper.find(UnknownView)).toHaveLength(0); + }); + + it("should show CreateElection view component with title for `/create-election/?title=test%20with%20title`", () => { + const wrapper = mount( + + + + ); + expect(wrapper.find(CreateElection)).toHaveLength(1); + expect(wrapper.find('input[name="title"]').props().defaultValue).toBe( + "test with title" + ); + expect(wrapper.find(UnknownView)).toHaveLength(0); + }); + + it("should show Vote view component for `/vote`", () => { + const wrapper = mount( + + + + ); + expect(wrapper.find(Vote)).toHaveLength(1); + expect(wrapper.find(UnknownView)).toHaveLength(0); + }); + + it("should show Result view component for `/result`", () => { + const wrapper = mount( + + + + ); + expect(wrapper.find(Result)).toHaveLength(1); + expect(wrapper.find(UnknownView)).toHaveLength(0); + }); + + it("should show UnknownView view component for `/aaabbbcccddd`", () => { + const wrapper = mount( + + + + ); + expect(wrapper.find(UnknownView)).toHaveLength(1); + }); }); diff --git a/src/Routes.jsx b/src/Routes.jsx index 6b08fa7..980af92 100644 --- a/src/Routes.jsx +++ b/src/Routes.jsx @@ -1,5 +1,5 @@ -import React from 'react'; -import {Switch, Route } from "react-router-dom"; +import React from "react"; +import { Switch, Route } from "react-router-dom"; import Home from "./components/views/Home"; import CreateElection from "./components/views/CreateElection"; @@ -11,18 +11,19 @@ import CreateSuccess from "./components/views/CreateSuccess"; import VoteSuccess from "./components/views/VoteSuccess"; function Routes() { - return (
- - - - - - - - - - -
+ return ( +
+ + + + + + + + + + +
); } diff --git a/src/Util.jsx b/src/Util.jsx index f4a3a17..779b66a 100644 --- a/src/Util.jsx +++ b/src/Util.jsx @@ -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] +})); diff --git a/src/components/form/ButtonWithConfirm.jsx b/src/components/form/ButtonWithConfirm.jsx index 7b16ca7..2a776af 100644 --- a/src/components/form/ButtonWithConfirm.jsx +++ b/src/components/form/ButtonWithConfirm.jsx @@ -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 ( -
- - -
{this.getComponent("modal-title")}
-
{this.getComponent("modal-body")}
-
{this.getComponent("modal-confirm")}
-
{this.getComponent("modal-cancel")}
-
-
- ); - } + } + + 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 ( +
+ + +
{this.getComponent("modal-title")}
+
{this.getComponent("modal-body")}
+
{this.getComponent("modal-confirm")}
+
{this.getComponent("modal-cancel")}
+
+
+ ); + } } -export default ButtonWithConfirm; \ No newline at end of file +export default ButtonWithConfirm; diff --git a/src/components/form/HelpButton.jsx b/src/components/form/HelpButton.jsx index 38a8621..b1bc596 100644 --- a/src/components/form/HelpButton.jsx +++ b/src/components/form/HelpButton.jsx @@ -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 ( - - - {this.state.tooltipOpen? - - {this.props.children} - :} - - - + hideTooltip = () => { + console.log("hide"); + this.setState({ + tooltipOpen: false + }); + }; - ); - } + render() { + return ( + + + {this.state.tooltipOpen ? ( + + + {this.props.children} + + ) : ( + + )} + + + + ); + } } -export default HelpButton; \ No newline at end of file +export default HelpButton; diff --git a/src/components/form/ModalConfirm.jsx b/src/components/form/ModalConfirm.jsx index 2695123..6e4fc56 100644 --- a/src/components/form/ModalConfirm.jsx +++ b/src/components/form/ModalConfirm.jsx @@ -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 ( - - {this.getComponent("title")} - - {this.getComponent("body")} - - - - - - - ); - } + render() { + return ( + + + {this.getComponent("title")} + + {this.getComponent("body")} + + + + + + ); + } } -export default ModalConfirm; \ No newline at end of file +export default ModalConfirm; diff --git a/src/components/layouts/Footer.jsx b/src/components/layouts/Footer.jsx index 653f396..408ea32 100644 --- a/src/components/layouts/Footer.jsx +++ b/src/components/layouts/Footer.jsx @@ -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( - - ) - } + render() { + return ( + + ); + } } -export default Footer; \ No newline at end of file +export default Footer; diff --git a/src/components/layouts/Header.jsx b/src/components/layouts/Header.jsx index e3b4c89..e6ef71a 100644 --- a/src/components/layouts/Header.jsx +++ b/src/components/layouts/Header.jsx @@ -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 ( -
- - -
-
- logo -
-
-
-

Plateforme de vote - Jugement Majoritaire -

- -
-
-
- - - - - -
-
- ); - } + render() { + return ( +
+ + +
+
+ logo +
+
+
+

+ Plateforme de vote + Jugement Majoritaire +

+
+
+
+ + + + + +
+
+ ); + } } -export default Header; \ No newline at end of file +export default Header; diff --git a/src/components/views/CreateElection.jsx b/src/components/views/CreateElection.jsx index 491e4db..5b5bec0 100644 --- a/src/components/views/CreateElection.jsx +++ b/src/components/views/CreateElection.jsx @@ -1,469 +1,663 @@ -import React, {Component} from "react"; -import { Redirect } from 'react-router-dom'; +import React, { Component } from "react"; +import { Redirect } from "react-router-dom"; import { - Collapse, - Container, - Row, - Col, - Input, - Label, - InputGroup, - InputGroupAddon, - Button, Card, CardBody -} from 'reactstrap'; - -import {toast, ToastContainer} from 'react-toastify'; -import 'react-toastify/dist/ReactToastify.css'; -import { resolve } from 'url'; + Collapse, + Container, + Row, + Col, + Input, + Label, + InputGroup, + InputGroupAddon, + Button, + Card, + CardBody +} from "reactstrap"; + +import { toast, ToastContainer } from "react-toastify"; +import "react-toastify/dist/ReactToastify.css"; +import { resolve } from "url"; import HelpButton from "../form/HelpButton"; -import {arrayMove, sortableContainer, sortableElement, sortableHandle} from 'react-sortable-hoc'; +import { + arrayMove, + sortableContainer, + sortableElement, + sortableHandle +} from "react-sortable-hoc"; import ButtonWithConfirm from "../form/ButtonWithConfirm"; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import {faPlus, faTrashAlt, faCheck, faCogs } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faPlus, + faTrashAlt, + faCheck, + faCogs +} from "@fortawesome/free-solid-svg-icons"; + +import { grades } from "../../Util"; +import { ReactMultiEmail, isEmail } from "react-multi-email"; +import "react-multi-email/style.css"; + +const DragHandle = sortableHandle(({ children }) => ( + {children} +)); + +const SortableCandidate = sortableElement(({ candidate, sortIndex, form }) => ( +
  • + + + + + + {sortIndex + 1} + + + form.editCandidateLabel(event, sortIndex)} + onKeyPress={event => + form.handleKeypressOnCandidateLabel(event, sortIndex) + } + placeholder="Nom du candidat ou de la proposition ..." + tabIndex={sortIndex + 1} + innerRef={ref => (form.candidateInputs[sortIndex] = ref)} + maxLength="250" + /> + +
    + +
    +
    Suppression ?
    +
    + Êtes-vous sûr de vouloir supprimer{" "} + {candidate.label !== "" ? ( + "{candidate.label}" + ) : ( + la ligne {sortIndex + 1} + )}{" "} + ? +
    +
    form.removeCandidate(sortIndex)} + > + Oui +
    +
    Non
    +
    +
    + + + + Saisissez ici le nom de votre candidat ou de votre proposition (250 + caractères max.) + + +
    +
  • +)); + +const SortableCandidatesContainer = sortableContainer(({ items, form }) => { + return ( +
      + {items.map((candidate, index) => ( + + ))} +
    + ); +}); -import { grades } from '../../Util'; -import { ReactMultiEmail, isEmail } from 'react-multi-email'; -import 'react-multi-email/style.css'; +class CreateElection extends Component { + constructor(props) { + super(props); + //default value : start now + const startedAt = new Date(); + const startedHour = startedAt.getHours(); + //default value : finish in one week + const finishedAt = new Date(startedAt.getTime() + 7 * 24 * 60 * 60 * 1000); + + this.state = { + candidates: [{ label: "" }, { label: "" }], + numCandidatesWithLabel: 0, + title: null, + isVisibleTipsDragAndDropCandidate: true, + numGrades: 7, + successCreate: false, + redirectTo: null, + isAdvancedOptionsOpen: false, + startedDayAt: startedAt.toISOString().substring(0, 10), + finishedDayAt: finishedAt.toISOString().substring(0, 10), + startedTimeAt: Math.floor(startedHour / 2) * 2 + ":00:00", + finishedTimeAt: "23:59:59", + electorEmails: [] + }; + this.candidateInputs = []; + this.focusInput = React.createRef(); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleChangeTitle = event => { + this.setState({ title: event.target.value }); + }; + + addCandidate = event => { + let candidates = this.state.candidates; + if (candidates.length < 100) { + candidates.push({ label: "" }); + this.setState({ candidates: candidates }); + } + if (event.type === "keypress") { + setTimeout(() => { + this.candidateInputs[this.state.candidates.length - 1].focus(); + }, 250); + } + }; + + removeCandidate = index => { + let candidates = this.state.candidates; + candidates.splice(index, 1); + console.log(candidates.length); + if (candidates.length === 0) { + candidates = [{ label: "" }]; + } + this.setState({ candidates: candidates }); + }; + + editCandidateLabel = (event, index) => { + let candidates = this.state.candidates; + let numLabels = 0; + candidates[index].label = event.currentTarget.value; + candidates.map((candidate, i) => { + if (candidate.label !== "") { + numLabels++; + } + return candidate.label; + }); + this.setState({ + candidates: candidates, + numCandidatesWithLabel: numLabels + }); + }; + + handleKeypressOnCandidateLabel = (event, index) => { + if (event.key === "Enter") { + event.preventDefault(); + if (index + 1 === this.state.candidates.length) { + this.addCandidate(event); + } else { + this.candidateInputs[index + 1].focus(); + } + } + }; + + onCandidatesSortEnd = ({ oldIndex, newIndex }) => { + let candidates = this.state.candidates; + candidates = arrayMove(candidates, oldIndex, newIndex); + this.setState({ candidates: candidates }); + }; + + handleChangeNumGrades = event => { + this.setState({ numGrades: event.target.value }); + }; + + toggleAdvancedOptions = () => { + this.setState({ isAdvancedOptionsOpen: !this.state.isAdvancedOptionsOpen }); + }; + + componentWillMount() { + const params = new URLSearchParams(this.props.location.search); + this.setState({ title: params.get("title") ? params.get("title") : "" }); + } + + handleSubmit() { + const { candidates, title, numGrades } = this.state; + + const endpoint = resolve(process.env.REACT_APP_SERVER_URL, "election/"); + + fetch(endpoint, { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify({ + title: title, + candidates: candidates.map(c => c.label), + on_invitation_only: this.state.electorEmails.length > 0, + num_grades: numGrades, + elector_emails: this.state.electorEmails, + started_at: this.state.startedDayAt + " " + this.state.startedTimeAt, + finished_at: this.state.finishedDayAt + " " + this.state.finishedTimeAt, + time_offset: new Date().getTimezoneOffset() + }) + }) + .then(response => response.json()) + .then(result => + this.setState(state => ({ + redirectTo: "/create-success/" + result.id, + successCreate: true + })) + ) + .catch(error => error); + } + + handleSendWithoutCandidate = () => { + toast.error("Vous devez saisir au moins deux candidats !", { + position: toast.POSITION.TOP_CENTER + }); + }; + + formatDate = (dateIsoStr, timeIsoStr) => { + let date = new Date(dateIsoStr + "T" + timeIsoStr); + let day = date.getDate(); + let month = date.getMonth() + 1; //Months are zero based + let year = date.getFullYear(); + let hours = date.getHours(); + let minutes = date.getMinutes(); + + if (month < 10) { + month = "0" + month; + } + if (day < 10) { + day = "0" + day; + } + if (hours < 10) { + hours = "0" + hours; + } + if (minutes < 10) { + minutes = "0" + minutes; + } + let hoursString = hours + "h" + minutes; + if (hoursString === "23h59") { + hoursString = "minuit"; + } + return day + "/" + month + "/" + year + " à " + hoursString; + }; -const DragHandle = sortableHandle(({children}) => {children}); + render() { + const { successCreate, redirectTo } = this.state; + const { electorEmails } = this.state; + const params = new URLSearchParams(this.props.location.search); -const SortableCandidate = sortableElement(({candidate, sortIndex, form}) =>
  • + if (successCreate) return ; - + return ( + + +
    + - - - - {sortIndex + 1} - - - form.editCandidateLabel(event, sortIndex)} - onKeyPress={(event) => form.handleKeypressOnCandidateLabel(event, sortIndex)} - placeholder="Nom du candidat ou de la proposition ..." - tabIndex={ sortIndex + 1} - innerRef={(ref) => form.candidateInputs[sortIndex] = ref} - maxLength="250"/> - -
    -
    Suppression ?
    -
    Êtes-vous sûr de vouloir supprimer {(candidate.label!=="")?"{candidate.label}":la ligne {sortIndex+1}} ? -
    -
    form.removeCandidate(sortIndex)}>Oui
    -
    Non
    -
    -
    +

    Démarrer un vote

    + +
    +
    + + + + + + - - Saisissez ici le nom de votre candidat ou de votre proposition (250 caractères max.) - + + Posez ici votre question ou introduisez simplement votre vote + (250 caractères max.) +
    + Par exemple :{" "} + Pour être mon représentant, je juge ce candidat ... +
    -
    - -
  • ); - -const SortableCandidatesContainer = sortableContainer(({items, form}) => { - return
      {items.map((candidate, index) => ( - - ))}
    ; -}); - -class CreateElection extends Component { - - constructor(props) { - super(props); - //default value : start now - const startedAt = new Date(); - const startedHour= startedAt.getHours(); - //default value : finish in one week - const finishedAt = new Date(startedAt.getTime() + 7 * 24 * 60 * 60 * 1000); - - this.state = { - candidates:[{label:""},{label:""}], - numCandidatesWithLabel:0, - title:null, - isVisibleTipsDragAndDropCandidate:true, - numGrades:7, - successCreate: false, - redirectTo: null, - isAdvancedOptionsOpen:false, - startedDayAt:startedAt.toISOString().substring(0, 10), - finishedDayAt:finishedAt.toISOString().substring(0, 10), - startedTimeAt:(Math.floor(startedHour/2)*2)+":00:00", - finishedTimeAt:"23:59:59", - electorEmails:[] - }; - this.candidateInputs = []; - this.focusInput= React.createRef(); - this.handleSubmit = this.handleSubmit.bind(this); - } - - - handleChangeTitle= (event) => { - this.setState({title: event.target.value}); - }; - - addCandidate = (event) => { - - let candidates = this.state.candidates; - if (candidates.length < 100) { - candidates.push({label:""}); - this.setState({ candidates: candidates}); - } - if(event.type === 'keypress'){ - setTimeout(()=>{ this.candidateInputs[this.state.candidates.length-1].focus();},250); - } - - }; - - removeCandidate = (index) => { - let candidates = this.state.candidates; - candidates.splice(index, 1); - console.log(candidates.length); - if(candidates.length===0){ - candidates=[{label:""}]; - } - this.setState({candidates: candidates}); - }; - - editCandidateLabel = (event, index) => { - let candidates = this.state.candidates; - let numLabels = 0; - candidates[index].label = event.currentTarget.value; - candidates.map((candidate,i)=>{ - if(candidate.label!==""){ - numLabels++; - } - return candidate.label; - }); - this.setState({candidates: candidates, numCandidatesWithLabel:numLabels}); - - }; - - handleKeypressOnCandidateLabel = (event, index) => { - if(event.key === 'Enter'){ - event.preventDefault(); - if(index+1===this.state.candidates.length){ - this.addCandidate(event); - }else{ - this.candidateInputs[index+1].focus(); - } - - } - - }; - - onCandidatesSortEnd = ({oldIndex, newIndex}) => { - let candidates = this.state.candidates; - candidates = arrayMove(candidates, oldIndex, newIndex); - this.setState({candidates: candidates}); - }; - - handleChangeNumGrades= (event) => { - this.setState({numGrades: event.target.value}); - }; - - toggleAdvancedOptions= () => { - this.setState({isAdvancedOptionsOpen: !this.state.isAdvancedOptionsOpen}); - }; - - componentWillMount() { - const params = new URLSearchParams(this.props.location.search); - this.setState({title:params.get("title")?params.get("title"):""}); - - }; - - handleSubmit () { - const { - candidates, - title, - numGrades - } = this.state; - - const endpoint = resolve( - process.env.REACT_APP_SERVER_URL, - 'election/' - ); - - fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - title: title, - candidates: candidates.map(c => c.label), - on_invitation_only: (this.state.electorEmails.length>0), - num_grades: numGrades, - elector_emails: this.state.electorEmails, - started_at:this.state.startedDayAt+" "+this.state.startedTimeAt, - finished_at:this.state.finishedDayAt+" "+this.state.finishedTimeAt, - time_offset:new Date().getTimezoneOffset() - }) - }) - .then(response => response.json()) - .then(result => this.setState(state => ({ - redirectTo: '/create-success/' + result.id, - successCreate: true - }))) - .catch(error => error); - }; - - handleSendWithoutCandidate = () => { - toast.error("Vous devez saisir au moins deux candidats !", { - position: toast.POSITION.TOP_CENTER - }); - }; - - formatDate = (dateIsoStr,timeIsoStr) => { - let date= new Date(dateIsoStr+"T"+timeIsoStr); - let day = date.getDate(); - let month = date.getMonth() + 1; //Months are zero based - let year = date.getFullYear(); - let hours = date.getHours(); - let minutes = date.getMinutes(); - - if(month<10){ - month="0"+month; - } - if(day<10){ - day="0"+day; - } - if(hours<10){ - hours="0"+hours; - } - if(minutes<10){ - minutes="0"+minutes; - } - let hoursString=hours+"h"+minutes; - if(hoursString==="23h59"){ - hoursString="minuit"; - } - - return day+"/"+month+"/"+year+" à "+hoursString; - }; - - render(){ - const { successCreate, redirectTo } = this.state; - const { electorEmails } = this.state; - const params = new URLSearchParams(this.props.location.search); - - - - if (successCreate) return ; - - return( - - - - -

    Démarrer un vote

    -
    -
    - - - - - - - - - - Posez ici votre question ou introduisez simplement votre vote (250 caractères max.) -
    Par exemple : Pour être mon représentant, je juge ce candidat ... -
    - -
    - - - - - - - - - - - - - - - - - - - - - - - - - this.setState({ startedDayAt: e.target.value })} /> - - - - - -
    - - - - - - this.setState({ finishedDayAt: e.target.value })} /> - - - - - -
    - - - - - - - - - - Vous pouvez choisir ici le nombre de mentions pour votre vote -
    Par exemple : 5 = Excellent, Très bien, bien, assez bien, passable -
    - - - { grades.map((mention,i) => { - return {mention.label} - }) - } - -
    -
    - - - - - - { - this.setState({ electorEmails: _emails }); - }} - validateEmail={email => { - return isEmail(email); // return boolean - }} - getLabel={( - email: string, - index: number, - removeEmail: (index: number) => void, - ) => { - return ( -
    - {email} - removeEmail(index)}>× -
    - ); - }} - /> -
    Liste des e-mails à préciser si vous désirez réaliser un vote fermé.
    - -
    -
    -
    -
    -
    - - - {this.state.numCandidatesWithLabel>=2? -
    Valider
    -
    Confirmez votre vote
    -
    -
    -
    Question du vote
    -
    {this.state.title}
    -
    Candidats/Propositions
    -
      - { - this.state.candidates.map((candidate,i) => { - if(candidate.label!==""){ - return
    • {candidate.label}
    • - }else{ - return
    • - } - - }) - } -
    -
    Dates
    -

    Le vote se déroulera du {this.formatDate(this.state.startedDayAt,this.state.startedTimeAt)} au {this.formatDate(this.state.finishedDayAt,this.state.finishedTimeAt)}

    -
    Mentions
    -
    { grades.map((mention,i) => { - return (i{mention.label}: - }) - }
    -
    Liste des électeurs
    -
    - {(electorEmails.length>0)?electorEmails.join(', '):

    Aucune adresse e-mail précisée.
    Le vote sera ouvert à tous les utilisateurs ayant le lien du vote

    } - -
    -
    -
    -
    Lancer le vote
    -
    Annuler
    -
    :} - -
    - -
    - ) - } + + + + + + + + + + + + + + + + + + + + + + + + + + + this.setState({ startedDayAt: e.target.value }) + } + /> + + + + + +
    + + + + + + + this.setState({ finishedDayAt: e.target.value }) + } + /> + + + + + +
    + + + + + + + + + + Vous pouvez choisir ici le nombre de mentions pour votre + vote +
    + Par exemple : {" "} + + {" "} + 5 = Excellent, Très bien, bien, assez bien, passable + +
    + + + {grades.map((mention, i) => { + return ( + + {mention.label} + + ); + })} + +
    +
    + + + + + + { + this.setState({ electorEmails: _emails }); + }} + validateEmail={email => { + return isEmail(email); // return boolean + }} + getLabel={( + email: string, + index: number, + removeEmail: (index: number) => void + ) => { + return ( +
    + {email} + removeEmail(index)} + > + × + +
    + ); + }} + /> +
    + + Liste des e-mails à préciser si vous désirez réaliser un + vote fermé. + +
    + +
    +
    +
    +
    +
    + + + {this.state.numCandidatesWithLabel >= 2 ? ( + +
    + + Valider +
    +
    Confirmez votre vote
    +
    +
    +
    + Question du vote +
    +
    + {this.state.title} +
    +
    + Candidats/Propositions +
    +
    +
      + {this.state.candidates.map((candidate, i) => { + if (candidate.label !== "") { + return ( +
    • + {candidate.label} +
    • + ); + } else { + return
    • ; + } + })} +
    +
    +
    + Dates +
    +

    + Le vote se déroulera du{" "} + + {this.formatDate( + this.state.startedDayAt, + this.state.startedTimeAt + )} + {" "} + au{" "} + + {this.formatDate( + this.state.finishedDayAt, + this.state.finishedTimeAt + )} + +

    +
    Mentions
    +
    + {grades.map((mention, i) => { + return i < this.state.numGrades ? ( + + {mention.label} + + ) : ( + + ); + })} +
    +
    + Liste des électeurs +
    +
    + {electorEmails.length > 0 ? ( + electorEmails.join(", ") + ) : ( +

    + Aucune adresse e-mail précisée. +
    + + Le vote sera ouvert à tous les utilisateurs ayant + le lien du vote + +

    + )} +
    +
    +
    +
    + Lancer le vote +
    +
    Annuler
    +
    + ) : ( + + )} + +
    + + + ); + } } export default CreateElection; diff --git a/src/components/views/CreateSuccess.js b/src/components/views/CreateSuccess.js index fbe12ca..8615ef9 100644 --- a/src/components/views/CreateSuccess.js +++ b/src/components/views/CreateSuccess.js @@ -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( - - - logo - - -

    Vote créé avec succès !

    -

    Vous pouvez maintenant partager le lien du vote aux participants :

    - - -
    - - -
    - -
    - -
    - -

    Voici le lien vers les résultats du vote en temps réel :

    -
    - - -
    - -
    - -
    - - - -
    - - -

    Conservez ces liens précieusement !

    -

    ATTENTION : 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.

    - -
    - - - - Participer maintenant ! - - -
    - ) - } + 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 ( + + + + logo + + + + +

    Vote créé avec succès !

    +

    + Vous pouvez maintenant partager le lien du vote aux participants : +

    + +
    + + +
    + +
    +
    + +

    + Voici le lien vers les résultats du vote en temps réel : +

    +
    + + +
    + +
    +
    + +
    + + +
    +

    + Conservez ces liens précieusement ! +

    +

    + ATTENTION : 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. +

    +
    + +
    + + + + + + Participer maintenant ! + + + +
    + ); + } } export default UnknownView; diff --git a/src/components/views/Home.jsx b/src/components/views/Home.jsx index 1f184e4..d342cad 100644 --- a/src/components/views/Home.jsx +++ b/src/components/views/Home.jsx @@ -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 - - - - - - - - - - -

    Pas de publicité et pas de cookie publicitaire.

    -
    - - - ) - }; + render() { + const redirect = this.state.redirect; + + if (redirect) { + return ( + + ); + } + return ( + +
    + + logo + + + +

    + Simple et gratuit : organisez un vote à l'aide du Jugement + Majoritaire. +

    + +
    + + + + + + + + + + +

    Pas de publicité et pas de cookie publicitaire.

    + +
    +
    +
    + ); + } } export default Home; diff --git a/src/components/views/Result.jsx b/src/components/views/Result.jsx index 570ed36..2f80dc1 100644 --- a/src/components/views/Result.jsx +++ b/src/components/views/Result.jsx @@ -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 ; } - toggleGraphics = () => { - this.setState(state => ({collapseGraphics: !state.collapseGraphics})); - }; + return ( + + + +

    {this.state.title}

    + +
    - toggleProfiles = () => { - this.setState(state => ({collapseProfiles: !state.collapseProfiles})); - }; + + +

    Résultat du vote :

    +
      + {candidates.map((candidate, i) => { + return ( +
    1. + {candidate.label} + + {candidate.score}% + + + {grades[candidate.grade].label} + +
    2. + ); + })} +
    + +
    - render() { - - const { redirectLost, - candidates, - electionGrades - } = this.state; - - if (redirectLost) { - return () - } - - return ( - - -

    {this.state.title}

    -
    - - -

    Résultat du vote :

    -
      {candidates.map((candidate, i) => { - return (
    1. {candidate.label}{candidate.score}%{grades[candidate.grade].label}
    2. ); - })}
    - -
    - - - - - -

    Graphique

    -
    - - -
    -
    - - {candidates.map((candidate, i) => { - return ( - - {/*candidate.label*/} - - ) - })}
    {i + 1} - - - - {candidate.profile.map((value, i) => { - if (value > 0) { - let percent = value + "%"; - if (i === 0) { - percent = "auto"; - } - return (); - }else{ - return null - } - - })} - -
     
    -
    - -
    -
    - - {candidates.map((candidate, i) => { - return ( - {(i > 0) ? ", " : ""}{i + 1}: {candidate.label}); - })} - -
    -
    - - {electionGrades.map((grade, i) => { - return ( - {grade.label} - )})} - -
    - - - - - - - - - -

    Profils - de mérites

    -
    - - -
    - - - - - {electionGrades.map((grade, i) => { - return (); - })} - - {candidates.map((candidate, i) => { - return ( - - {/*candidate.label*/} - {candidate.profile.map((value, i) => { - return (); - })} - ) - })} -
    #{grade.label}
    {i + 1}{value}%
    -
    - {candidates.map((candidate, i) => { - return ({(i > 0) ? ", " : ""}{i + 1}: {candidate.label}); - })} -
    -
    -
    - -
    - - ) - } + + + + +

    + Graphique +

    +
    + + +
    +
    + + + {candidates.map((candidate, i) => { + return ( + + + {/*candidate.label*/} + + + ); + })} + +
    {i + 1} + + + + {candidate.profile.map((value, i) => { + if (value > 0) { + let percent = value + "%"; + if (i === 0) { + percent = "auto"; + } + return ( + + ); + } else { + return null; + } + })} + + +
    +   +
    +
    +
    +
    + + {candidates.map((candidate, i) => { + return ( + + {i > 0 ? ", " : ""} + {i + 1}: {candidate.label} + + ); + })} + +
    +
    + + {electionGrades.map((grade, i) => { + return ( + + {grade.label} + + ); + })} + +
    + + + + + + + + + +

    + Profils de mérites +

    +
    + + +
    + + + + + {electionGrades.map((grade, i) => { + return ( + + ); + })} + + + + {candidates.map((candidate, i) => { + return ( + + + {/*candidate.label*/} + {candidate.profile.map((value, i) => { + return ; + })} + + ); + })} + +
    # + + {grade.label}{" "} + +
    {i + 1}{value}%
    +
    + + {candidates.map((candidate, i) => { + return ( + + {i > 0 ? ", " : ""} + {i + 1}: {candidate.label} + + ); + })} + +
    +
    +
    + +
    + + ); + } } export default Result; diff --git a/src/components/views/UnknownElection.js b/src/components/views/UnknownElection.js index 8ff1aff..2fe4560 100644 --- a/src/components/views/UnknownElection.js +++ b/src/components/views/UnknownElection.js @@ -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( - - - logo - - -

    Oups ! Ce vote n'existe pas ou n'est plus disponible.

    -

    N'hésitez pas à démarrer un nouveau vote

    -
    - - - Revenir à l'accueil - - -
    - ) - } + render() { + return ( + + + + logo + + + + +

    Oups ! Ce vote n'existe pas ou n'est plus disponible.

    +

    N'hésitez pas à démarrer un nouveau vote

    + +
    + + + + Revenir à l'accueil + + + +
    + ); + } } export default UnknownElection; diff --git a/src/components/views/UnknownView.jsx b/src/components/views/UnknownView.jsx index cde603d..7b2484b 100644 --- a/src/components/views/UnknownView.jsx +++ b/src/components/views/UnknownView.jsx @@ -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( - - - logo - - -

    Oups ! Cette page n'existe pas

    -
    - - - Revenir à l'accueil - - -
    - ) - } + render() { + return ( + + + + logo + + + + +

    Oups ! Cette page n'existe pas

    + +
    + + + + Revenir à l'accueil + + + +
    + ); + } } export default UnknownView; diff --git a/src/components/views/Vote.jsx b/src/components/views/Vote.jsx index 690a1ce..583acd2 100644 --- a/src/components/views/Vote.jsx +++ b/src/components/views/Vote.jsx @@ -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 (); - } - - return( - - -
    - -

    { this.state.title }

    -
    - -
     
    - { electionGrades.map((grade,j) => { - return (j{grade.label}:null; - }) - } -
    - - { - candidates.map((candidate,i) => { - return - -
    {candidate.label}

    - { this.state.electionGrades.map((grade,j) => { - return (j - - :null - }) - } -
    - }) - - } - - - - {(this.state.ratedCandidates.length!==this.state.candidates.length)?:} - - - -
    -
    - ) + if (redirectTo) { + return ; } + + return ( + + +
    + + +

    {this.state.title}

    + +
    + + +
     
    + + {electionGrades.map((grade, j) => { + return j < this.state.numGrades ? ( + + + {grade.label} + + + ) : null; + })} +
    + + {candidates.map((candidate, i) => { + return ( + + +
    {candidate.label}
    +
    + + {this.state.electionGrades.map((grade, j) => { + return j < this.state.numGrades ? ( + + + + ) : null; + })} +
    + ); + })} + + + + {this.state.ratedCandidates.length !== + this.state.candidates.length ? ( + + ) : ( + + )} + + +
    +
    + ); + } } export default Vote; diff --git a/src/components/views/VoteSuccess.js b/src/components/views/VoteSuccess.js index 2e74211..f5ffe5f 100644 --- a/src/components/views/VoteSuccess.js +++ b/src/components/views/VoteSuccess.js @@ -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 ( + + + + logo + + + + +

    Participation enregistrée avec succès !

    +

    Merci pour votre participation.

    + +
    - render(){ - return( - - - - logo - - - - -

    Participation enregistrée avec succès !

    -

    Merci pour votre participation.

    - - -
    - - - - Revenir à l'accueil - - -
    - ) - } + + + + Revenir à l'accueil + + + +
    + ); + } } export default UnknownView; diff --git a/src/index.jsx b/src/index.jsx index 58a3428..2a9db7a 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -1,11 +1,10 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import './scss/config.scss'; -import App from './App'; -import * as serviceWorker from './serviceWorker'; +import React from "react"; +import ReactDOM from "react-dom"; +import "./scss/config.scss"; +import App from "./App"; +import * as serviceWorker from "./serviceWorker"; - -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render(, document.getElementById("root")); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. diff --git a/src/scss/_app.scss b/src/scss/_app.scss index 08211f5..4b418c5 100644 --- a/src/scss/_app.scss +++ b/src/scss/_app.scss @@ -9,121 +9,118 @@ $body-bg: #000000 !default; $body-color: $mv-light-color !default; $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 ) !default; -.logo-text > h1{ - font-size:16px; - font-weight:bold; - margin:0; - line-height:1; +.logo-text > h1 { + font-size: 16px; + font-weight: bold; + margin: 0; + line-height: 1; } -.bold{ - font-weight:bold; +.bold { + font-weight: bold; } -.logo-text > h1 > small{ - display:block; +.logo-text > h1 > small { + display: block; letter-spacing: 0.09em; } -html, body, #root, #root > div{ - height:100%; +html, +body, +#root, +#root > div { + height: 100%; } - - -main{ - background-image:url("/background-mv.png"); - background-size:100%; +main { + background-image: url("/background-mv.png"); + background-size: 100%; background-attachment: fixed; - background-repeat:no-repeat; - background-color:$mv-blue-color; + background-repeat: no-repeat; + background-color: $mv-blue-color; min-height: calc(100% - 106px); overflow: auto; - padding-top:72px; - padding-bottom:36px; + padding-top: 72px; + padding-bottom: 36px; } -header{ - position:fixed; - z-index:10; - width:100%; +header { + position: fixed; + z-index: 10; + width: 100%; } -footer{ - background-color:$body-bg; - color:$mv-light-color; - padding:25px; +footer { + background-color: $body-bg; + color: $mv-light-color; + padding: 25px; } -footer a{ - color:$mv-light-color; +footer a { + color: $mv-light-color; } -footer a:hover{ - color:#fff; +footer a:hover { + color: #fff; } -hr{ - border:none; - border-top:1px solid $mv-light-color; - width:100%; - margin:auto; +hr { + border: none; + border-top: 1px solid $mv-light-color; + width: 100%; + margin: auto; } - -ul.sortable,li.sortable{ - padding:0; - margin:0 0 0 0; +ul.sortable, +li.sortable { + padding: 0; + margin: 0 0 0 0; list-style-type: none; } -li.sortable{ - margin:0 0 15px 0; +li.sortable { + margin: 0 0 15px 0; } -.pointer{ +.pointer { cursor: pointer; } - -.modal{ - color:$mv-dark-color; +.modal { + color: $mv-dark-color; } /* card Vote */ -.cardVote{ - background-color:$mv-light-color; - margin:1em 0; - color:$mv-dark-color; - border-radius:0.15em; - padding:1em 0; +.cardVote { + background-color: $mv-light-color; + margin: 1em 0; + color: $mv-dark-color; + border-radius: 0.15em; + padding: 1em 0; } -.cardVote .nowrap{ - white-space: nowrap;; +.cardVote .nowrap { + white-space: nowrap; } -.cardVote hr{ - border-top:1px solid $mv-dark-color; - margin:10px 0; +.cardVote hr { + border-top: 1px solid $mv-dark-color; + margin: 10px 0; } -.cardVote.row:hover{ - background-color:$mv-light-color-hover; +.cardVote.row:hover { + background-color: $mv-light-color-hover; } - /* checkbox */ /* The radio */ .radio { - display: block; position: relative; padding-left: 30px; @@ -133,7 +130,7 @@ li.sortable{ -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; - user-select: none + user-select: none; } /* Hide the browser's default radio button */ @@ -145,20 +142,18 @@ li.sortable{ /* Create a custom radio button */ .checkround { - position: absolute; top: 6px; left: 0; height: 20px; width: 20px; - background-color: #fff ; - border-color:$mv-blue-color; - border-style:solid; - border-width:2px; + background-color: #fff; + border-color: $mv-blue-color; + border-style: solid; + border-width: 2px; border-radius: 50%; } - /* When the radio button is checked, add a blue background */ .radio input:checked ~ .checkround { background-color: #fff; @@ -183,9 +178,7 @@ li.sortable{ width: 12px; height: 12px; border-radius: 50%; - background:$mv-blue-color; - - + background: $mv-blue-color; } /* The check */ @@ -213,15 +206,15 @@ li.sortable{ /* Create a custom checkbox */ .checkmark { position: absolute; - margin-left: calc( 50% - 12px); + margin-left: calc(50% - 12px); top: 0; left: 0; height: 24px; width: 24px; - background-color: #fff ; - border-color:$mv-blue-color; - border-style:solid; - border-width:2px; + background-color: #fff; + border-color: $mv-blue-color; + border-style: solid; + border-width: 2px; } @include media-breakpoint-down(md) { @@ -234,7 +227,7 @@ li.sortable{ /* When the checkbox is checked, add a blue background */ .check input:checked ~ .checkmark { - background-color: #fff ; + background-color: #fff; } /* Create the checkmark/indicator (hidden when not checked) */ @@ -255,65 +248,64 @@ li.sortable{ top: 1px; width: 10px; height: 15px; - border: solid ; - border-color:#fff; + border: solid; + border-color: #fff; border-width: 0 3px 3px 0; -webkit-transform: rotate(45deg); -ms-transform: rotate(45deg); transform: rotate(45deg); - cursor:pointer; + cursor: pointer; } -.cust-btn{ +.cust-btn { margin-bottom: 10px; background-color: $mv-blue-color; border-width: 2px; border-color: $mv-blue-color; color: #fff; } -.cust-btn:hover{ - +.cust-btn:hover { border-color: $mv-blue-color; background-color: #fff; color: $mv-blue-color; border-radius: 20px; transform-style: 2s; - } - - - /** collapse **/ .panel-title:after { - content:'+'; - float:right; - font-size:28px; - font-weight:900; + content: "+"; + float: right; + font-size: 28px; + font-weight: 900; } .panel-title.collapsed:after { - content:'-'; + content: "-"; } /** table profiles **/ -.profiles thead,.profiles tbody,.profiles tr,.profiles th, .profiles td,.profiles thead th{ - border-color:$mv-blue-color; - color:$mv-blue-color; +.profiles thead, +.profiles tbody, +.profiles tr, +.profiles th, +.profiles td, +.profiles thead th { + border-color: $mv-blue-color; + color: $mv-blue-color; } -.median{ - border-width:0 3px 0 0; - border-style:dashed; - border-color:$mv-blue-color; - min-height:30px; +.median { + border-width: 0 3px 0 0; + border-style: dashed; + border-color: $mv-blue-color; + min-height: 30px; width: 1px; position: absolute; margin-left: 50%; margin-top: -15px; } - /** react multi email **/ .react-multi-email > span[data-placeholder] { padding: 0.25em !important; -} \ No newline at end of file +} diff --git a/src/scss/_bootstrap.scss b/src/scss/_bootstrap.scss index b431718..48fa0ce 100644 --- a/src/scss/_bootstrap.scss +++ b/src/scss/_bootstrap.scss @@ -1,4 +1 @@ -@import '~bootstrap/scss/bootstrap.scss'; - - - +@import "~bootstrap/scss/bootstrap.scss"; diff --git a/src/scss/config.scss b/src/scss/config.scss index 6ac2e69..4b3d95c 100644 --- a/src/scss/config.scss +++ b/src/scss/config.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'; \ No newline at end of file +@import "_bootstrap.scss"; +@import "app.scss"; diff --git a/src/serviceWorker.jsx b/src/serviceWorker.jsx index f8c7e50..5ef2083 100644 --- a/src/serviceWorker.jsx +++ b/src/serviceWorker.jsx @@ -11,9 +11,9 @@ // opt-in, read https://bit.ly/CRA-PWA const isLocalhost = Boolean( - window.location.hostname === 'localhost' || + window.location.hostname === "localhost" || // [::1] is the IPv6 localhost address. - window.location.hostname === '[::1]' || + window.location.hostname === "[::1]" || // 127.0.0.1/8 is considered localhost for IPv4. window.location.hostname.match( /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ @@ -21,7 +21,7 @@ const isLocalhost = Boolean( ); export function register(config) { - if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { + if (process.env.NODE_ENV === "production" && "serviceWorker" in navigator) { // The URL constructor is available in all browsers that support SW. const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); if (publicUrl.origin !== window.location.origin) { @@ -31,7 +31,7 @@ export function register(config) { return; } - window.addEventListener('load', () => { + window.addEventListener("load", () => { const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; if (isLocalhost) { @@ -42,8 +42,8 @@ export function register(config) { // service worker/PWA documentation. navigator.serviceWorker.ready.then(() => { console.log( - 'This web app is being served cache-first by a service ' + - 'worker. To learn more, visit https://bit.ly/CRA-PWA' + "This web app is being served cache-first by a service " + + "worker. To learn more, visit https://bit.ly/CRA-PWA" ); }); } else { @@ -64,14 +64,14 @@ function registerValidSW(swUrl, config) { return; } installingWorker.onstatechange = () => { - if (installingWorker.state === 'installed') { + if (installingWorker.state === "installed") { if (navigator.serviceWorker.controller) { // At this point, the updated precached content has been fetched, // but the previous service worker will still serve the older // content until all client tabs are closed. console.log( - 'New content is available and will be used when all ' + - 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' + "New content is available and will be used when all " + + "tabs for this page are closed. See https://bit.ly/CRA-PWA." ); // Execute callback @@ -82,7 +82,7 @@ function registerValidSW(swUrl, config) { // At this point, everything has been precached. // It's the perfect time to display a // "Content is cached for offline use." message. - console.log('Content is cached for offline use.'); + console.log("Content is cached for offline use."); // Execute callback if (config && config.onSuccess) { @@ -94,7 +94,7 @@ function registerValidSW(swUrl, config) { }; }) .catch(error => { - console.error('Error during service worker registration:', error); + console.error("Error during service worker registration:", error); }); } @@ -103,10 +103,10 @@ function checkValidServiceWorker(swUrl, config) { fetch(swUrl) .then(response => { // Ensure service worker exists, and that we really are getting a JS file. - const contentType = response.headers.get('content-type'); + const contentType = response.headers.get("content-type"); if ( response.status === 404 || - (contentType != null && contentType.indexOf('javascript') === -1) + (contentType != null && contentType.indexOf("javascript") === -1) ) { // No service worker found. Probably a different app. Reload the page. navigator.serviceWorker.ready.then(registration => { @@ -121,13 +121,13 @@ function checkValidServiceWorker(swUrl, config) { }) .catch(() => { console.log( - 'No internet connection found. App is running in offline mode.' + "No internet connection found. App is running in offline mode." ); }); } export function unregister() { - if ('serviceWorker' in navigator) { + if ("serviceWorker" in navigator) { navigator.serviceWorker.ready.then(registration => { registration.unregister(); });