From 080f59b10189aec385886eaf35acee1c6d066064 Mon Sep 17 00:00:00 2001 From: jimmys-box <89916651+jimmys-box@users.noreply.github.com> Date: Mon, 7 Feb 2022 10:30:12 +0100 Subject: [PATCH] new: --- components/Bulles.jsx | 166 +++++ components/Chart.tsx | 41 ++ components/ChartWrapper.js | 21 + components/CopyField.jsx | 69 ++ components/D3Chart.js | 38 ++ components/Error.jsx | 40 ++ components/LoadingScreen.js | 75 +++ components/Modal.jsx | 62 ++ components/SystemeVote.jsx | 37 ++ components/banner/Facebook.jsx | 29 + components/banner/Gform.jsx | 25 + components/banner/Helloasso.jsx | 24 + components/banner/Paypal.jsx | 44 ++ components/flag.js | 4 + components/form/AddPicture.jsx | 37 ++ components/form/AlertButton.jsx | 24 + components/form/ButtonWithConfirm.jsx | 57 ++ components/form/CandidateField.jsx | 143 ++++ components/form/CandidatesField.jsx | 124 ++++ components/form/ConfirmModal.jsx | 164 +++++ components/form/HelpButton.jsx | 75 +++ components/layouts/Footer.jsx | 83 +++ components/layouts/Header.jsx | 125 ++++ components/layouts/HeaderMobile.jsx | 61 ++ components/layouts/LanguageSelector.jsx | 33 + .../layouts/result/HeaderDesktopResult.jsx | 47 ++ .../layouts/result/HeaderMobileResult.jsx | 38 ++ components/layouts/result/HeaderResult.jsx | 14 + components/layouts/useBbox.jsx | 20 + components/loader/index.jsx | 14 + components/plot.js | 18 + components/wait/index.jsx | 8 + components/wait/loader-pulse-2-alpha.gif | Bin 0 -> 26560 bytes components/wait/loader-pulse-2.gif | Bin 0 -> 32793 bytes config/env.js | 93 +++ config/jest/cssTransform.js | 14 + config/jest/fileTransform.js | 40 ++ config/modules.js | 84 +++ config/paths.js | 90 +++ config/pnpTs.js | 35 + config/webpack.config.js | 628 ++++++++++++++++++ config/webpackDevServer.config.js | 104 +++ functions/send-invite-email/invite-en.html | 218 ++++++ functions/send-invite-email/invite-en.txt | 19 + functions/send-invite-email/invite-fr.html | 203 ++++++ functions/send-invite-email/invite-fr.txt | 17 + functions/send-invite-email/invite.html | 218 ++++++ functions/send-invite-email/invite.txt | 19 + .../send-invite-email/send-invite-email.js | 150 +++++ 49 files changed, 3692 insertions(+) create mode 100644 components/Bulles.jsx create mode 100644 components/Chart.tsx create mode 100644 components/ChartWrapper.js create mode 100644 components/CopyField.jsx create mode 100644 components/D3Chart.js create mode 100644 components/Error.jsx create mode 100644 components/LoadingScreen.js create mode 100644 components/Modal.jsx create mode 100644 components/SystemeVote.jsx create mode 100644 components/banner/Facebook.jsx create mode 100644 components/banner/Gform.jsx create mode 100644 components/banner/Helloasso.jsx create mode 100644 components/banner/Paypal.jsx create mode 100644 components/flag.js create mode 100644 components/form/AddPicture.jsx create mode 100644 components/form/AlertButton.jsx create mode 100644 components/form/ButtonWithConfirm.jsx create mode 100644 components/form/CandidateField.jsx create mode 100644 components/form/CandidatesField.jsx create mode 100644 components/form/ConfirmModal.jsx create mode 100644 components/form/HelpButton.jsx create mode 100644 components/layouts/Footer.jsx create mode 100644 components/layouts/Header.jsx create mode 100644 components/layouts/HeaderMobile.jsx create mode 100644 components/layouts/LanguageSelector.jsx create mode 100644 components/layouts/result/HeaderDesktopResult.jsx create mode 100644 components/layouts/result/HeaderMobileResult.jsx create mode 100644 components/layouts/result/HeaderResult.jsx create mode 100644 components/layouts/useBbox.jsx create mode 100644 components/loader/index.jsx create mode 100644 components/plot.js create mode 100644 components/wait/index.jsx create mode 100644 components/wait/loader-pulse-2-alpha.gif create mode 100644 components/wait/loader-pulse-2.gif create mode 100644 config/env.js create mode 100644 config/jest/cssTransform.js create mode 100644 config/jest/fileTransform.js create mode 100644 config/modules.js create mode 100644 config/paths.js create mode 100644 config/pnpTs.js create mode 100644 config/webpack.config.js create mode 100644 config/webpackDevServer.config.js create mode 100644 functions/send-invite-email/invite-en.html create mode 100644 functions/send-invite-email/invite-en.txt create mode 100644 functions/send-invite-email/invite-fr.html create mode 100644 functions/send-invite-email/invite-fr.txt create mode 100644 functions/send-invite-email/invite.html create mode 100644 functions/send-invite-email/invite.txt create mode 100644 functions/send-invite-email/send-invite-email.js diff --git a/components/Bulles.jsx b/components/Bulles.jsx new file mode 100644 index 0000000..5e5d23d --- /dev/null +++ b/components/Bulles.jsx @@ -0,0 +1,166 @@ +import React from 'react'; +import plotly from 'plotly.js/dist/plotly'; +import createPlotComponent from 'react-plotly.js/factory'; +import LoadingScreen from "./LoadingScreen"; + +function Bulles (props) { + +// récupération des résultats de l'élection et stockage en tableau +const votesBrut = (Object.values(props))[0]; + +// déclaration et initialisation des mentions et couleurs +const mentionsBrut = ['Passable', 'Assez bien', 'Bien', 'Très bien', 'Excellent']; +const couleursBrut = ['#BB9C42', '#AABA44', '#DCDF44', '#B3D849', '#61AD45']; + +//----------- Traitement des données -----------// + +// fonction d'inversement des éléments de tableau +function inverse(obj){ + var retobj = {}; + for(var key in obj){ + retobj[obj[key]] = key; + } + return retobj; + } + +// fonction de réduction d'amplitude permettant de conserver une représentation ordinale du nombre de votes sans décalage visuel trop important +/* +Pattern de calcul : + +Soient Ai, Bi, Ci, Di, Ei les nombres de votes initiaux fournis dans le tableau classé par ordre mélioratif de mention (de Passable à Excellent). Il vient : +A = 1 +B = <{[1 + (Bi/Ai)] / 40} * A> +C = <{[1 + (Ci/Bi)] / 40} * B> +D = <{[1 + (Di/Ci)] / 40} * C> +E = <{[1 + (Ei/Di)] / 40} * D> +*/ +function redAmpli(tab) { + var nvTab = []; + nvTab[0] = 100; + + for(i = 1; i < tab.length; i++) { + nvTab[i] = ( (1 + ((tab[i]/tab[(i-1)]) / 40 ) ) * nvTab[(i-1)]); + } + return nvTab; +} + + +// déclaration de l'objet votes-mention et votes-couleur +var votesMentionNonOrdonnes = {}; +var votesCouleurNonOrdonnes = {}; + +// initialisation votes-mention ordonnés croissants +for (var i = 0; i < mentionsBrut.length; i++) { + votesMentionNonOrdonnes[votesBrut[i]] = mentionsBrut[i]; + votesCouleurNonOrdonnes[votesBrut[i]] = couleursBrut[i]; +} + +// déclaration des mentions-votes par ordre croissant +var votesMentionOrdonnes = inverse(votesMentionNonOrdonnes); +var votesCouleurOrdonnes = inverse(votesCouleurNonOrdonnes); + +// vérification du nombre de votes classés par ordre croissant et passés initialement en propriétés au composant +console.log("Les données transmises au composant concernant le nombre de votes par mention sont : "); +console.log(votesBrut); + +// vérification des mentions destinées à être associées aux votes et ordonnées initialement par ordre mélioratif +console.log("Les mentions des votes sont classées initialement par ordre mélioratif de la façon suivante :"); +console.log(mentionsBrut); + +// vérification du nombre de votes classés par ordre croissant +console.log("Les mentions-votes classées par ordre croissant de votes sont : "); +console.log(votesMentionOrdonnes); + +// séparation des mentions et des votes +const mentions = Object.keys(votesMentionOrdonnes); +const votes = Object.values(votesMentionOrdonnes); +const couleurs = Object.keys(votesCouleurOrdonnes); + +// vérification des mentions et des votes prêts à être traités pour la représentation graphique +console.log('La liste des mentions issue du classement par ordre croissant de votes est :'); +console.log(mentions); +console.log('La liste du nombre de votes correspondant, classée par ordre croissant, est :'); +console.log(votes); + +// déclaration et initialisation des rayons de bulle pour la représentation graphique +var rayons = []; +rayons = redAmpli(votes) + +// vérification des rayons +console.log('La liste des rayons à représenter graphiquement est la suivante :'); +console.log(rayons); + +// déclaration et initialisation des textes des bulles +const texteBulle1 = (mentions[0] + "
" + votes[0] + " votes").toString(); +const texteBulle2 = (mentions[1] + "
" + votes[1] + " votes").toString(); +const texteBulle3 = (mentions[2] + "
" + votes[2] + " votes").toString(); +const texteBulle4 = (mentions[3] + "
" + votes[3] + " votes").toString(); +const texteBulle5 = (mentions[4] + "
" + votes[4] + " votes").toString(); + +// déclaration et initialisation d'une instance de graphique en bulles +// const Plot = createPlotComponent(plotly); +const Plot = require('react-plotly.js').default; + +//---------------------------------------------// + + + +//----------- Affichage des données -----------// +const [loading, setLoading] = React.useState(true); + React.useEffect(() =>{ + setTimeout(() => setLoading(false), 3000); + }) +return ( + + //
+ // {!loading ? ( + // + %{text}' + + '', + text: [texteBulle1, texteBulle2, texteBulle3, texteBulle4, texteBulle5], + showlegend: false, + mode: 'markers', + marker: { + color: [couleurs[0], couleurs[1], couleurs[2], couleurs[3], couleurs[4]], + size: rayons + } + } + ]} + layout={ { + width: 600, + height: 600, + title: 'Nombre de voix par candidat', + xaxis: { + showgrid: false, + showticklabels: false, + showline: false, + zeroline: false, + range: [0, 1] + }, + yaxis: { + showgrid: false, + showticklabels: false, + showline: false, + zeroline: false, + range: [0, 1] + } + } } + config={{ + displayModeBar: false // this is the line that hides the bar. + }} +/> +// +// ) : ( +// +// )} +//
+) +} + +export default Bulles; \ No newline at end of file diff --git a/components/Chart.tsx b/components/Chart.tsx new file mode 100644 index 0000000..c093c58 --- /dev/null +++ b/components/Chart.tsx @@ -0,0 +1,41 @@ +import * as React from "react"; +import * as d3 from "d3"; + +function drawChart(svgRef: React.RefObject) { + const data = [12, 5, 6, 6, 9, 10]; + const h = 120; + const w = 250; + const svg = d3.select(svgRef.current); + + svg + .attr("width", w) + .attr("height", h) + .style("margin-top", 50) + .style("margin-left", 50); + + svg + .selectAll("rect") + .data(data) + .enter() + .append("rect") + .attr("x", (d, i) => i * 40) + .attr("y", (d, i) => h - 10 * d) + .attr("width", 20) + .attr("height", (d, i) => d * 10) + .attr("fill", "steelblue"); +} + +const Chart: React.FunctionComponent = () => { + const svg = React.useRef(null); + React.useEffect(() => { + drawChart(svg); + }, [svg]); + + return ( +
+ +
+ ); +}; + +export default Chart; diff --git a/components/ChartWrapper.js b/components/ChartWrapper.js new file mode 100644 index 0000000..de849cd --- /dev/null +++ b/components/ChartWrapper.js @@ -0,0 +1,21 @@ +import React, { useRef, useState, useEffect } from 'react'; +import D3Chart from './D3Chart'; + +const ChartWrapper = () => { + + const chartArea = useRef(null); + const [chart, setChart] = useState(null); + + useEffect(() => { + if (!chart) { + setChart(new D3Chart(chartArea.current)); + } + }, [chart]); + + return ( +
+ ); + +} + +export default ChartWrapper; \ No newline at end of file diff --git a/components/CopyField.jsx b/components/CopyField.jsx new file mode 100644 index 0000000..bc37856 --- /dev/null +++ b/components/CopyField.jsx @@ -0,0 +1,69 @@ +/* eslint react/prop-types: 0 */ +import React from "react"; +import { Button } from "reactstrap"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faCopy, + faVoteYea, + faExclamationTriangle, + faExternalLinkAlt, +} from "@fortawesome/free-solid-svg-icons"; + +const CopyField = (props) => { + const ref = React.createRef(); + const handleClickOnField = (event) => { + event.target.focus(); + event.target.select(); + }; + const handleClickOnButton = () => { + const input = ref.current; + input.focus(); + input.select(); + document.execCommand("copy"); + }; + + const { t, value, iconCopy, iconOpen } = props; + + return ( +
+ + +
+ {/* + + */} + +
+
+ ); +}; + +CopyField.defaultProps = { + iconCopy: faCopy, + iconOpen: faExternalLinkAlt, +}; + +export default CopyField; diff --git a/components/D3Chart.js b/components/D3Chart.js new file mode 100644 index 0000000..dd62855 --- /dev/null +++ b/components/D3Chart.js @@ -0,0 +1,38 @@ +import * as d3 from 'd3'; + +const url = "https://udemy-react-d3.firebaseio.com/tallest_men.json"; + +const WIDTH = 800; +const HEIGHT = 500; + +export default class D3Chart { + constructor(element) { + const svg = d3.select(element) + .append("svg") + .attr("width", 800) + .attr("height", 500) + + d3.json(url).then(data => { + const max = d3.max(data, d => d.height) + const y = d3.scaleLinear() + .domain([0, max]) + .range([0, HEIGHT]) + + const x = d3.scaleBand() + .domain(data.map(d => d.name)) + .range([0, WIDTH]) + .padding(0.4) + + const rects = svg.selectAll("rect") + .data(data) + + rects.enter() + .append("rect") + .attr("x", d => x(d.name)) + .attr("y", d => HEIGHT - y(d.height)) + .attr("width", x.bandwidth) + .attr("height", d => y(d.height)) + .attr("fill", "grey") + }) + } +} \ No newline at end of file diff --git a/components/Error.jsx b/components/Error.jsx new file mode 100644 index 0000000..dc855db --- /dev/null +++ b/components/Error.jsx @@ -0,0 +1,40 @@ +import Link from "next/link"; +import { Container, Row, Col } from "reactstrap"; +import { useTranslation } from "next-i18next"; + +const Error = (props) => { + const { t } = useTranslation(); + return ( + + + + + logo + + + + + +

{props.value}

+ +
+ + + + {t("common.backHomepage")} + + + + + {t("resource.help")} + + + +
+ ); +}; + +export default Error; diff --git a/components/LoadingScreen.js b/components/LoadingScreen.js new file mode 100644 index 0000000..c148442 --- /dev/null +++ b/components/LoadingScreen.js @@ -0,0 +1,75 @@ +import React from "react" +import styled from "styled-components" + +const Screen = styled.div` + position: relative; + + opacity: 0; + animation: fade 0.4s ease-in forwards; + background: black; + + @keyframes fade { + 0% { + opacity: 0.4; + } + 50% { + opacity: 0.8; + } + 100% { + opacity: 1; + } + } +`; + +const Balls = styled.div` + display: flex; + + + .ball { + height: 20px; + width: 20px; + border-radius: 50%; + background: red; + margin: 0 6px 0 0; + animation: oscillate 0.7s ease-in forwards infinite; + } + + .one { + animation-delay: 0.5s; + } + .two { + animation-delay: 1s; + } + .three { + animation-delay: 2s; + } + + @keyframes oscillate { + 0% { + transform: translateY(0); + } + 50% { + transform: translateY(20px); + } + 100% { + transform: translateY(0); + } + } +`; + + + +const LoadingScreen = () => { + return ( + + + +
+
+
+
+
+ ); +}; + +export default LoadingScreen; diff --git a/components/Modal.jsx b/components/Modal.jsx new file mode 100644 index 0000000..c502450 --- /dev/null +++ b/components/Modal.jsx @@ -0,0 +1,62 @@ +import React, { useEffect, useRef, useState } from "react"; +import ReactDOM from "react-dom"; +import styled from "styled-components"; + +const Modal = ({ show, onClose, children, title }) => { + + const handleCloseClick = (e) => { + e.preventDefault(); + onClose(); + }; + + const modalContent = show ? ( + + + + + x + + + {title && {title}} + {children} + + + ) : null; + + + return ( + modalContent + ); + + }; + + const StyledModalBody = styled.div` + padding-top: 10px; + `; + + const StyledModalHeader = styled.div` + display: flex; + justify-content: flex-end; + font-size: 25px; + `; + + const StyledModal = styled.div` + background: white; + width: 500px; + height: 600px; + border-radius: 15px; + padding: 15px; + `; + const StyledModalOverlay = styled.div` + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + background-color: rgba(0, 0, 0, 0.5); + `; + + export default Modal; \ No newline at end of file diff --git a/components/SystemeVote.jsx b/components/SystemeVote.jsx new file mode 100644 index 0000000..62df611 --- /dev/null +++ b/components/SystemeVote.jsx @@ -0,0 +1,37 @@ +import React, {Fragment} from 'react'; +import Head from 'next/head'; +import dynamic from 'next/dynamic'; + + +const Bulles = dynamic(import('./Bulles'), { + ssr: false +}) + +const nbVotesPassables = 15; +const nbVotesAssezBien = 200; +const nbVotesBien = 389; +const nbVotesTresBien = 12; +const nbVotesExcellent = 2; + +const resultats = [nbVotesPassables, nbVotesAssezBien, nbVotesBien, nbVotesTresBien, nbVotesExcellent]; + +var totalVotes = 0; + +for(var i = 0; i < resultats.length; i++) { + totalVotes += resultats[i]; +} + +function SystemeVote() { + + + return ( + + + +

Le total des votes est de {totalVotes}.

+ +
+ ); +} + +export default SystemeVote; \ No newline at end of file diff --git a/components/banner/Facebook.jsx b/components/banner/Facebook.jsx new file mode 100644 index 0000000..e710a23 --- /dev/null +++ b/components/banner/Facebook.jsx @@ -0,0 +1,29 @@ +/* eslint react/prop-types: 0 */ +import React from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faFacebookSquare } from "@fortawesome/free-brands-svg-icons"; + +const Facebook = props => { + const handleClick = () => { + const url = + "https://www.facebook.com/sharer.php?u=" + + props.url + + "&t=" + + props.title; + window.open( + url, + "", + "menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=500,width=700" + ); + }; + return ( + + ); +}; + +export default Facebook; + +//i diff --git a/components/banner/Gform.jsx b/components/banner/Gform.jsx new file mode 100644 index 0000000..cd20281 --- /dev/null +++ b/components/banner/Gform.jsx @@ -0,0 +1,25 @@ +import PropTypes from 'prop-types'; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {faCommentAlt} from "@fortawesome/free-solid-svg-icons"; +import {api} from "@services/api" + + +const Gform = (props) => { + return ( + + + Votre avis nous intéresse ! + + ); +} + +Gform.propTypes = { + className: PropTypes.string, +}; + +export default Gform; diff --git a/components/banner/Helloasso.jsx b/components/banner/Helloasso.jsx new file mode 100644 index 0000000..eeaa595 --- /dev/null +++ b/components/banner/Helloasso.jsx @@ -0,0 +1,24 @@ +/* eslint react/prop-types: 0 */ +import React from "react"; +import i18n from "../../i18n"; + +const Helloasso = props => { + const locale = + i18n.language.substring(0, 2).toLowerCase() === "fr" ? "fr" : "en"; + const linkHelloAssoBanner = + locale === "fr" + ? "https://www.helloasso.com/associations/mieux-voter/formulaires/1/widget" + : "https://www.helloasso.com/associations/mieux-voter/formulaires/1/widget/en"; + + return ( + + support us on helloasso + + ); +}; + +export default Helloasso; diff --git a/components/banner/Paypal.jsx b/components/banner/Paypal.jsx new file mode 100644 index 0000000..fdec7dc --- /dev/null +++ b/components/banner/Paypal.jsx @@ -0,0 +1,44 @@ +import {useTranslation} from "next-i18next"; +import {useRouter} from "next/router" +import {faPaypal} from "@fortawesome/free-brands-svg-icons"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; + +const Paypal = () => { + const {t} = useTranslation(); + + // FIXME generate a xx_XX string for locale version + const {locale} = useRouter(); + let localeShort = locale.substring(0, 2); + let localeComplete = + localeShort.toLowerCase() + "_" + localeShort.toUpperCase(); + if (localeComplete === "en_EN") { + localeComplete = "en_US"; + } + const pixelLink = + `https://www.paypal.com/${localeComplete}/i/scr/pixel.gif`; + + return ( +
+
+ + + + +
+
+ ); +}; + +export default Paypal; diff --git a/components/flag.js b/components/flag.js new file mode 100644 index 0000000..df68eaf --- /dev/null +++ b/components/flag.js @@ -0,0 +1,4 @@ +import * as React from "react"; +import FlagIconFactory from "react-flag-icon-css"; + +export const FlagIcon = FlagIconFactory(React, { useCssModules: false }); diff --git a/components/form/AddPicture.jsx b/components/form/AddPicture.jsx new file mode 100644 index 0000000..b858e18 --- /dev/null +++ b/components/form/AddPicture.jsx @@ -0,0 +1,37 @@ +import { useState } from "react"; + + + +export default function AddPicture(props) { + const [image, setImage] = useState(null); + const [createObjectURL, setCreateObjectURL] = useState(null); + + const uploadToClient = (event) => { + if (event.target.files && event.target.files[0]) { + const i = event.target.files[0]; + + setImage(i); + setCreateObjectURL(URL.createObjectURL(i)); + } + }; + + return ( +
+
+
+ +
+
+
+

Photo (facultatif)

+ +

Importer une photo.
format : jpg, png, pdf

+
+ + +
+
+
+ + ); +} \ No newline at end of file diff --git a/components/form/AlertButton.jsx b/components/form/AlertButton.jsx new file mode 100644 index 0000000..10c1e0a --- /dev/null +++ b/components/form/AlertButton.jsx @@ -0,0 +1,24 @@ +import { useState } from 'react' +import { Alert, Button } from 'react-bootstrap'; +import { faTimes, faExclamationCircle } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +export default function AlertDismissibleExample() { + const [show, setShow] = useState(true); + + if (show) { + return ( + + +
+ + 2 candidats minimum +
+ setShow(false)} icon={faTimes} className="mr-2" /> +
+ +
+ ); + } + return null; +} + diff --git a/components/form/ButtonWithConfirm.jsx b/components/form/ButtonWithConfirm.jsx new file mode 100644 index 0000000..4dc46db --- /dev/null +++ b/components/form/ButtonWithConfirm.jsx @@ -0,0 +1,57 @@ +import {useState} from "react"; +import { + faTrashAlt, +} from "@fortawesome/free-solid-svg-icons"; +import {Button, Modal, ModalHeader, ModalBody, ModalFooter} from "reactstrap"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {useTranslation} from "next-i18next"; + +const ButtonWithConfirm = ({className, label, onDelete}) => { + const [visibled, setVisibility] = useState(false); + const {t} = useTranslation(); + + const toggle = () => setVisibility(!visibled) + + return ( +
+ + + {t("Delete?")} + + {t("Are you sure to delete")}{" "} + {label && label !== "" ? ( + "{label}" + ) : ( + <>{t("the row")} + )}{" "} + ? + + + + + + +
+ ); +} + +export default ButtonWithConfirm; diff --git a/components/form/CandidateField.jsx b/components/form/CandidateField.jsx new file mode 100644 index 0000000..08b3c78 --- /dev/null +++ b/components/form/CandidateField.jsx @@ -0,0 +1,143 @@ +import { useState } from 'react' +import ButtonWithConfirm from "./ButtonWithConfirm"; +import { + Row, + Col, + Label, + Input, + InputGroup, + InputGroupAddon, + Button, Modal, ModalHeader, ModalBody, ModalFooter +} from "reactstrap"; +import { useTranslation } from "react-i18next"; +import { + sortableHandle +} from "react-sortable-hoc"; +import HelpButton from "@components/form/HelpButton"; +import AddPicture from "@components/form/AddPicture"; +import { + faPlus, faCogs, faCheck +} from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +const DragHandle = sortableHandle(({ children }) => ( + {children} +)); + +const CandidateField = ({ label, description, candIndex, onDelete, onAdd, ...inputProps }) => { + const { t } = useTranslation(); + const [visibled, setVisibility] = useState(false); + const toggle = () => setVisibility(!visibled) + const test = () => { + toggle(); + onAdd(); + } + const [image, setImage] = useState(null); + const [createObjectURL, setCreateObjectURL] = useState(null); + + const uploadToClient = (event) => { + if (event.target.files && event.target.files[0]) { + const i = event.target.files[0]; + + setImage(i); + setCreateObjectURL(URL.createObjectURL(i)); + } + }; + return ( + +
+
+ + Ajouter un candidat + +
+ +
+ + + + + + + + + + + + + + + + + + + + +
Ajouter un participant
+

Ajoutez une photo, le nom et une description au candidat.

+
+
+
+ +
+
+
+

Photo (facultatif)

+ +

Importer une photo.
format : jpg, png, pdf

+
+ + +
+
+
+ +
+
+ + + + + + + + +
+ +
+ {/* + + {t( + "Enter the name of your candidate or proposal here (250 characters max.)" + )} + + */} +
+ ); +} + +export default CandidateField diff --git a/components/form/CandidatesField.jsx b/components/form/CandidatesField.jsx new file mode 100644 index 0000000..1fdefea --- /dev/null +++ b/components/form/CandidatesField.jsx @@ -0,0 +1,124 @@ +import {useState, useEffect, createRef} from 'react' +import {useTranslation} from "react-i18next"; +import { + Button, + Card, + CardBody +} from "reactstrap"; +import { + faPlus, +} from "@fortawesome/free-solid-svg-icons"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import { + sortableContainer, + sortableElement, + sortableHandle +} from "react-sortable-hoc"; +import arrayMove from "array-move" +import CandidateField from './CandidateField' +import AlertDismissibleExample from './AlertButton' +// const SortableItem = sortableElement(({className, ...childProps}) =>
  • ); +// +// const SortableContainer = sortableContainer(({children}) => { +// return ; +// }); + +const SortableItem = ({className, ...childProps}) =>
  • ; + +const SortableContainer = ({children}) => { + return ; +}; + + +const CandidatesField = ({onChange}) => { + const {t} = useTranslation(); + const [candidates, setCandidates] = useState([]) + + const addCandidate = () => { + if (candidates.length < 1000) { + candidates.push({label: "", description: "", fieldRef: createRef()}); + setCandidates([...candidates]); + onChange(candidates) + } else { + console.error("Too many candidates") + } + }; + + useEffect(() => { + addCandidate(); + addCandidate(); + }, []) + + + const removeCandidate = index => { + if (candidates.length === 1) { + const newCandidates = [] + newCandidates.push({label: "", description: "", fieldRef: createRef()}); + newCandidates.push({label: "", description: "", fieldRef: createRef()}); + setCandidates(newCandidates); + onChange(newCandidates) + } + else { + const newCandidates = candidates.filter((c, i) => i != index) + setCandidates(newCandidates); + onChange(newCandidates); + } + }; + + const editCandidate = (index, label, description) => { + candidates[index].label = label + candidates[index].description = description + setCandidates([...candidates]); + onChange(candidates); + }; + + const handleKeyPress = (e, index) => { + if (e.key === "Enter") { + e.preventDefault(); + if (index + 1 === candidates.length) { + addCandidate(); + } + else { + candidates[index + 1].fieldRef.current.focus(); + } + } + } + + const onSortEnd = ({oldIndex, newIndex}) => { + setCandidates(arrayMove(candidates, oldIndex, newIndex)); + }; + + return ( +
    +
    +

    Saisissez ici le nom de vos candidats.

    + + + {candidates.map((candidate, index) => { + const className = "sortable" + return ( + removeCandidate(index)} + onChange={(e) => editCandidate(index, e.target.value)} + onKeyPress={(e) => handleKeyPress(e, index)} + onAdd={addCandidate} + innerRef={candidate.fieldRef} + /> + ) + })} + +
    +
    + ); + +} + + +export default CandidatesField + diff --git a/components/form/ConfirmModal.jsx b/components/form/ConfirmModal.jsx new file mode 100644 index 0000000..fcc23fd --- /dev/null +++ b/components/form/ConfirmModal.jsx @@ -0,0 +1,164 @@ +import {useTranslation} from "next-i18next"; +import {useState} from "react"; +import { + faExclamationTriangle, + faCheck, +} from "@fortawesome/free-solid-svg-icons"; +import {Button, Modal, ModalHeader, ModalBody, ModalFooter} from "reactstrap"; +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; + +const ConfirmModal = ({tabIndex, title, candidates, grades, isTimeLimited, start, finish, emails, restrictResult, className, confirmCallback}) => { + const [visibled, setVisibility] = useState(false); + const {t} = useTranslation(); + const toggle = () => setVisibility(!visibled) + + return ( +
    + + + + {t("Confirm your vote")} + + +
    +
    + {t("Question of the election")} +
    +
    {title}
    +
    + {t("Candidates/Proposals")} +
    +
    +
      + {candidates.map((candidate, i) => { + if (candidate.label !== "") { + return ( +
    • + {candidate.label} +
    • + ); + } else { + return
    • ; + } + })} +
    +
    +
    +
    + {t("Dates")} +
    +
    + {t("The election will take place from")}{" "} + + {start.toLocaleDateString()}, {t("at")}{" "} + {start.toLocaleTimeString()} + {" "} + {t("to")}{" "} + + {finish.toLocaleDateString()}, {t("at")}{" "} + {finish.toLocaleTimeString()} + +
    +
    +
    + {t("Grades")} +
    +
    + {grades.map((mention, i) => { + return i < grades.length ? ( + + {mention.label} + + ) : ( + + ); + })} +
    +
    + {t("Voters' list")} +
    +
    + {emails.length > 0 ? ( + emails.join(", ") + ) : ( +

    + {t("The form contains no address.")} +
    + + {t( + "The election will be opened to anyone with the link" + )} + +

    + )} +
    + {restrictResult ? ( +
    +
    +
    + + {t("Results available at the close of the vote")} +
    +

    + + {t( + "The results page will not be accessible until the end date is reached." + )}{" "} + ({finish.toLocaleDateString()} {t("at")}{" "} + {finish.toLocaleTimeString()}) + +

    +
    +
    + ) : ( +
    +
    +
    + {t("Results available at any time")} +
    +
    +
    + )} +
    +
    + + + + +
    +
    + ) +} + +export default ConfirmModal diff --git a/components/form/HelpButton.jsx b/components/form/HelpButton.jsx new file mode 100644 index 0000000..1d87b53 --- /dev/null +++ b/components/form/HelpButton.jsx @@ -0,0 +1,75 @@ +/* eslint react/prop-types: 0 */ +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 + }; + } + + showTooltip = () => { + this.setState({ + tooltipOpen: true + }); + }; + + hideTooltip = () => { + this.setState({ + tooltipOpen: false + }); + }; + + render() { + return ( + + + {this.state.tooltipOpen ? ( + + + {this.props.children} + + ) : ( + + )} + + + + ); + } +} +export default HelpButton; diff --git a/components/layouts/Footer.jsx b/components/layouts/Footer.jsx new file mode 100644 index 0000000..86c789f --- /dev/null +++ b/components/layouts/Footer.jsx @@ -0,0 +1,83 @@ +import Link from "next/link"; +import { useTranslation } from "next-i18next"; +import Paypal from "../banner/Paypal"; +import { useBbox } from "./useBbox"; +import { Button, Row, Col } from "reactstrap"; +import LanguageSelector from "./LanguageSelector"; + +const Footer = () => { + const linkStyle = { whiteSpace: "nowrap" }; + const { t } = useTranslation(); + + const [bboxLink1, link1] = useBbox(); + const [bboxLink2, link2] = useBbox(); + const [bboxLink3, link3] = useBbox(); + const [bboxLink4, link4] = useBbox(); + const [bboxLink5, link5] = useBbox(); + + return ( + + ); +}; +export default Footer; diff --git a/components/layouts/Header.jsx b/components/layouts/Header.jsx new file mode 100644 index 0000000..270208d --- /dev/null +++ b/components/layouts/Header.jsx @@ -0,0 +1,125 @@ +/* eslint react/prop-types: 0 */ +import { useState } from "react"; +import { + Collapse, + Navbar, + NavbarToggler, + Nav, + NavItem, + Button, +} from "reactstrap"; +import Link from "next/link"; +import { useTranslation } from "next-i18next"; +import LanguageSelector from "./LanguageSelector"; +import Accordion from "react-bootstrap/Accordion"; + +const Header = () => { + const [isOpen, setOpen] = useState(false); + + const toggle = () => setOpen(!isOpen); + + const { t } = useTranslation("common"); + + return ( +
    + +
    + + +
    + + + + +
    +
    + ); +}; + +export default Header; diff --git a/components/layouts/HeaderMobile.jsx b/components/layouts/HeaderMobile.jsx new file mode 100644 index 0000000..f738561 --- /dev/null +++ b/components/layouts/HeaderMobile.jsx @@ -0,0 +1,61 @@ +/* eslint react/prop-types: 0 */ +import {useState} from "react"; +import {Collapse, Navbar, NavbarToggler, Nav, NavItem} from "reactstrap"; +import Link from "next/link"; +import Head from "next/head"; +import {useTranslation} from 'next-i18next' +import {FontAwesomeIcon} from "@fortawesome/react-fontawesome"; +import {faRocket} from "@fortawesome/free-solid-svg-icons"; +import LanguageSelector from "./LanguageSelector"; + + +const Header = () => { + const [isOpen, setOpen] = useState(false) + + const toggle = () => setOpen(!isOpen); + + const {t} = useTranslation() + return ( + <> + {t("title")} +
    + + + +
    +
    + logo +
    +
    +
    +

    + {t("Voting platform")} + {t("Majority Judgment")} +

    +
    +
    +
    +
    + + + + + +
    +
    + + ); +} + +export default Header; diff --git a/components/layouts/LanguageSelector.jsx b/components/layouts/LanguageSelector.jsx new file mode 100644 index 0000000..e850fc7 --- /dev/null +++ b/components/layouts/LanguageSelector.jsx @@ -0,0 +1,33 @@ +import {useRouter} from 'next/router' +import ReactFlagsSelect from 'react-flags-select'; + +const LanguageSelector = () => { + + const router = useRouter(); + let localeShort = router.locale.substring(0, 2).toUpperCase(); + if (localeShort === "EN") localeShort = "GB"; + + const selectHandler = e => { + let locale = e.toLowerCase(); + if (locale === "gb") locale = "en"; + router.push("", "", {locale}) + }; + return ( + + ); +}; + +export default LanguageSelector; diff --git a/components/layouts/result/HeaderDesktopResult.jsx b/components/layouts/result/HeaderDesktopResult.jsx new file mode 100644 index 0000000..2d4e457 --- /dev/null +++ b/components/layouts/result/HeaderDesktopResult.jsx @@ -0,0 +1,47 @@ +/* eslint react/prop-types: 0 */ +import { useState } from "react"; +import { Container, Row, Col, Nav, NavItem } from "reactstrap"; +import Link from "next/link"; +import Head from "next/head"; +import { useTranslation } from 'next-i18next' + + + +export default function HeaderResultResult() { + + ; + return ( + + + + + + + + +

    Clos il y a 2 jours

    +
    + + +

    14 votants

    +
    + + + +

    Quel est le meilleur candidat pour les éléctions présidentielle ?

    + + + + +

    Télécharger les résultats

    +
    + +

    Partagez les résultats

    +
    + +
    + +
    + ); +} + diff --git a/components/layouts/result/HeaderMobileResult.jsx b/components/layouts/result/HeaderMobileResult.jsx new file mode 100644 index 0000000..772d826 --- /dev/null +++ b/components/layouts/result/HeaderMobileResult.jsx @@ -0,0 +1,38 @@ +/* eslint react/prop-types: 0 */ +import { useState } from "react"; +import { Container, Row, Col, Nav, NavItem } from "reactstrap"; +import Link from "next/link"; +import Head from "next/head"; +import { useTranslation } from 'next-i18next' + + +export default function HeaderMobileResult() { + + ; + return ( + + + + +

    Quel est le meilleur candidat pour les éléctions présidentielle ?

    +
    + + + +

    Clos il y a 2 jours

    + + + + +

    14 votants

    + +
    + + + + + +
    + ); +} + diff --git a/components/layouts/result/HeaderResult.jsx b/components/layouts/result/HeaderResult.jsx new file mode 100644 index 0000000..121f03f --- /dev/null +++ b/components/layouts/result/HeaderResult.jsx @@ -0,0 +1,14 @@ + +import React from 'react'; +import HeaderDesktopResult from './HeaderDesktopResult'; +import HeaderMobileResult from './HeaderMobileResult'; +import { useMediaQuery } from "react-responsive"; + +export default function HeaderResult() { + + const isMobile = useMediaQuery({ query: "(max-width: 800px)" }); + + if (isMobile) return ; + + else return ; +} \ No newline at end of file diff --git a/components/layouts/useBbox.jsx b/components/layouts/useBbox.jsx new file mode 100644 index 0000000..42e11f8 --- /dev/null +++ b/components/layouts/useBbox.jsx @@ -0,0 +1,20 @@ +/* eslint react/prop-types: 0 */ +import { useState } from 'react'; +import { useRef } from 'react'; +import { useEffect } from 'react'; + +export const useBbox = () => { + const ref = useRef(); + const [bbox, setBbox] = useState({}); + + const set = () => + setBbox(ref && ref.current ? ref.current.getBoundingClientRect() : {}); + + useEffect(() => { + set(); + window.addEventListener('resize', set); + return () => window.removeEventListener('resize', set); + }, []); + + return [bbox, ref]; +}; \ No newline at end of file diff --git a/components/loader/index.jsx b/components/loader/index.jsx new file mode 100644 index 0000000..5de0aba --- /dev/null +++ b/components/loader/index.jsx @@ -0,0 +1,14 @@ +import React from "react"; +import Image from 'next/image' + +const Loader = () => { + return ( +
    + Loading... + + +
    + ); +}; + +export default Loader; diff --git a/components/plot.js b/components/plot.js new file mode 100644 index 0000000..8b326e2 --- /dev/null +++ b/components/plot.js @@ -0,0 +1,18 @@ +import React from 'react'; +import plotly from 'plotly.js/dist/plotly'; +import createPlotComponent from 'react-plotly.js/factory'; + +// const Plot = createPlotComponent(plotly); +const Plot = require('react-plotly.js').default; +export default () => ( + +) diff --git a/components/wait/index.jsx b/components/wait/index.jsx new file mode 100644 index 0000000..9765a71 --- /dev/null +++ b/components/wait/index.jsx @@ -0,0 +1,8 @@ +import React from "react"; +import Loader from "../loader"; + +const Wait = () => { + return ; +}; + +export default Wait; diff --git a/components/wait/loader-pulse-2-alpha.gif b/components/wait/loader-pulse-2-alpha.gif new file mode 100644 index 0000000000000000000000000000000000000000..a2fc0a2f847adee315b845532f970548fce7ebd7 GIT binary patch literal 26560 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

    *cn4_-R7Bg79ICK`Ww*^;4iAvW}bX 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 literal 0 HcmV?d00001 diff --git a/config/env.js b/config/env.js new file mode 100644 index 0000000..b0344c5 --- /dev/null +++ b/config/env.js @@ -0,0 +1,93 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const paths = require('./paths'); + +// Make sure that including paths.js after env.js will read .env variables. +delete require.cache[require.resolve('./paths')]; + +const NODE_ENV = process.env.NODE_ENV; +if (!NODE_ENV) { + throw new Error( + 'The NODE_ENV environment variable is required but was not specified.' + ); +} + +// https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use +var dotenvFiles = [ + `${paths.dotenv}.${NODE_ENV}.local`, + `${paths.dotenv}.${NODE_ENV}`, + // Don't include `.env.local` for `test` environment + // since normally you expect tests to produce the same + // results for everyone + NODE_ENV !== 'test' && `${paths.dotenv}.local`, + paths.dotenv, +].filter(Boolean); + +// Load environment variables from .env* files. Suppress warnings using silent +// if this file is missing. dotenv will never modify any environment variables +// that have already been set. Variable expansion is supported in .env files. +// https://github.com/motdotla/dotenv +// https://github.com/motdotla/dotenv-expand +dotenvFiles.forEach(dotenvFile => { + if (fs.existsSync(dotenvFile)) { + require('dotenv-expand')( + require('dotenv').config({ + path: dotenvFile, + }) + ); + } +}); + +// We support resolving modules according to `NODE_PATH`. +// This lets you use absolute paths in imports inside large monorepos: +// https://github.com/facebook/create-react-app/issues/253. +// It works similar to `NODE_PATH` in Node itself: +// https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders +// Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored. +// Otherwise, we risk importing Node.js core modules into an app instead of Webpack shims. +// https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421 +// We also resolve them to make sure all tools using them work consistently. +const appDirectory = fs.realpathSync(process.cwd()); +process.env.NODE_PATH = (process.env.NODE_PATH || '') + .split(path.delimiter) + .filter(folder => folder && !path.isAbsolute(folder)) + .map(folder => path.resolve(appDirectory, folder)) + .join(path.delimiter); + +// Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be +// injected into the application via DefinePlugin in Webpack configuration. +const REACT_APP = /^REACT_APP_/i; + +function getClientEnvironment(publicUrl) { + const raw = Object.keys(process.env) + .filter(key => REACT_APP.test(key)) + .reduce( + (env, key) => { + env[key] = process.env[key]; + return env; + }, + { + // Useful for determining whether we’re running in production mode. + // Most importantly, it switches React into the correct mode. + NODE_ENV: process.env.NODE_ENV || 'development', + // Useful for resolving the correct path to static assets in `public`. + // For example, . + // This should only be used as an escape hatch. Normally you would put + // images into the `src` and `import` them in code to get their paths. + PUBLIC_URL: publicUrl, + } + ); + // Stringify all values so we can feed into Webpack DefinePlugin + const stringified = { + 'process.env': Object.keys(raw).reduce((env, key) => { + env[key] = JSON.stringify(raw[key]); + return env; + }, {}), + }; + + return { raw, stringified }; +} + +module.exports = getClientEnvironment; diff --git a/config/jest/cssTransform.js b/config/jest/cssTransform.js new file mode 100644 index 0000000..8f65114 --- /dev/null +++ b/config/jest/cssTransform.js @@ -0,0 +1,14 @@ +'use strict'; + +// This is a custom Jest transformer turning style imports into empty objects. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process() { + return 'module.exports = {};'; + }, + getCacheKey() { + // The output is always the same. + return 'cssTransform'; + }, +}; diff --git a/config/jest/fileTransform.js b/config/jest/fileTransform.js new file mode 100644 index 0000000..74dc1aa --- /dev/null +++ b/config/jest/fileTransform.js @@ -0,0 +1,40 @@ +'use strict'; + +const path = require('path'); +const camelcase = require('camelcase'); + +// This is a custom Jest transformer turning file imports into filenames. +// http://facebook.github.io/jest/docs/en/webpack.html + +module.exports = { + process(src, filename) { + const assetFilename = JSON.stringify(path.basename(filename)); + + if (filename.match(/\.svg$/)) { + // Based on how SVGR generates a component name: + // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6 + const pascalCaseFileName = camelcase(path.parse(filename).name, { + pascalCase: true, + }); + const componentName = `Svg${pascalCaseFileName}`; + return `const React = require('react'); + module.exports = { + __esModule: true, + default: ${assetFilename}, + ReactComponent: React.forwardRef(function ${componentName}(props, ref) { + return { + $$typeof: Symbol.for('react.element'), + type: 'svg', + ref: ref, + key: null, + props: Object.assign({}, props, { + children: ${assetFilename} + }) + }; + }), + };`; + } + + return `module.exports = ${assetFilename};`; + }, +}; diff --git a/config/modules.js b/config/modules.js new file mode 100644 index 0000000..4646eb0 --- /dev/null +++ b/config/modules.js @@ -0,0 +1,84 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const paths = require('./paths'); +const chalk = require('react-dev-utils/chalk'); + +/** + * Get the baseUrl of a compilerOptions object. + * + * @param {Object} options + */ +function getAdditionalModulePaths(options = {}) { + const baseUrl = options.baseUrl; + + // We need to explicitly check for null and undefined (and not a falsy value) because + // TypeScript treats an empty string as `.`. + if (baseUrl == null) { + // If there's no baseUrl set we respect NODE_PATH + // Note that NODE_PATH is deprecated and will be removed + // in the next major release of create-react-app. + + const nodePath = process.env.NODE_PATH || ''; + return nodePath.split(path.delimiter).filter(Boolean); + } + + const baseUrlResolved = path.resolve(paths.appPath, baseUrl); + + // We don't need to do anything if `baseUrl` is set to `node_modules`. This is + // the default behavior. + if (path.relative(paths.appNodeModules, baseUrlResolved) === '') { + return null; + } + + // Allow the user set the `baseUrl` to `appSrc`. + if (path.relative(paths.appSrc, baseUrlResolved) === '') { + return [paths.appSrc]; + } + + // Otherwise, throw an error. + throw new Error( + chalk.red.bold( + "Your project's `baseUrl` can only be set to `src` or `node_modules`." + + ' Create React App does not support other values at this time.' + ) + ); +} + +function getModules() { + // Check if TypeScript is setup + const hasTsConfig = fs.existsSync(paths.appTsConfig); + const hasJsConfig = fs.existsSync(paths.appJsConfig); + + if (hasTsConfig && hasJsConfig) { + throw new Error( + 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.' + ); + } + + let config; + + // If there's a tsconfig.json we assume it's a + // TypeScript project and set up the config + // based on tsconfig.json + if (hasTsConfig) { + config = require(paths.appTsConfig); + // Otherwise we'll check if there is jsconfig.json + // for non TS projects. + } else if (hasJsConfig) { + config = require(paths.appJsConfig); + } + + config = config || {}; + const options = config.compilerOptions || {}; + + const additionalModulePaths = getAdditionalModulePaths(options); + + return { + additionalModulePaths: additionalModulePaths, + hasTsConfig, + }; +} + +module.exports = getModules(); diff --git a/config/paths.js b/config/paths.js new file mode 100644 index 0000000..f23c121 --- /dev/null +++ b/config/paths.js @@ -0,0 +1,90 @@ +'use strict'; + +const path = require('path'); +const fs = require('fs'); +const url = require('url'); + +// Make sure any symlinks in the project folder are resolved: +// https://github.com/facebook/create-react-app/issues/637 +const appDirectory = fs.realpathSync(process.cwd()); +const resolveApp = relativePath => path.resolve(appDirectory, relativePath); + +const envPublicUrl = process.env.PUBLIC_URL; + +function ensureSlash(inputPath, needsSlash) { + const hasSlash = inputPath.endsWith('/'); + if (hasSlash && !needsSlash) { + return inputPath.substr(0, inputPath.length - 1); + } else if (!hasSlash && needsSlash) { + return `${inputPath}/`; + } else { + return inputPath; + } +} + +const getPublicUrl = appPackageJson => + envPublicUrl || require(appPackageJson).homepage; + +// We use `PUBLIC_URL` environment variable or "homepage" field to infer +// "public path" at which the app is served. +// Webpack needs to know it to put the right