import { useState, useEffect } from "react"; import Head from "next/head"; import { useRouter } from "next/router"; import { useTranslation } from "next-i18next"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import { Collapse, Container, Row, Col, Input, Label, InputGroup, InputGroupAddon, Button, Card, CardBody, } from "reactstrap"; import { ReactMultiEmail, isEmail } from "react-multi-email"; import "react-multi-email/style.css"; import { toast, ToastContainer } from "react-toastify"; import "react-toastify/dist/ReactToastify.css"; import queryString from "query-string"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faPlus, faTrashAlt, faCheck, faCogs, faExclamationTriangle, } from "@fortawesome/free-solid-svg-icons"; import { useAppContext } from "@services/context"; import { createElection } from "@services/api"; import { translateGrades } from "@services/grades"; import HelpButton from "@components/form/HelpButton"; import Loader from "@components/wait"; import CandidatesField from "@components/form/CandidatesField"; import ConfirmModal from "@components/form/ConfirmModal"; import config from "../../next-i18next.config.js"; // Error messages const AT_LEAST_2_CANDIDATES_ERROR = "Please add at least 2 candidates."; const NO_TITLE_ERROR = "Please add a title."; const isValidDate = (date) => date instanceof Date && !isNaN(date); const getOnlyValidDate = (date) => (isValidDate(date) ? date : new Date()); // Convert a Date object into YYYY-MM-DD const dateToISO = (date) => getOnlyValidDate(date).toISOString().substring(0, 10); // Retrieve the current hour, minute, sec, ms, time into a timestamp const hours = (date) => getOnlyValidDate(date).getHours() * 3600 * 1000; const minutes = (date) => getOnlyValidDate(date).getMinutes() * 60 * 1000; const seconds = (date) => getOnlyValidDate(date).getSeconds() * 1000; const ms = (date) => getOnlyValidDate(date).getMilliseconds(); const time = (date) => hours(getOnlyValidDate(date)) + minutes(getOnlyValidDate(date)) + seconds(getOnlyValidDate(date)) + ms(getOnlyValidDate(date)); // Retrieve the time part from a timestamp and remove the day. Return a int. const timeMinusDate = (date) => time(getOnlyValidDate(date)); // Retrieve the day and remove the time. Return a Date const dateMinusTime = (date) => new Date(getOnlyValidDate(date).getTime() - time(getOnlyValidDate(date))); const displayClockOptions = () => Array(24) .fill(1) .map((x, i) => ( )); export const getStaticProps = async ({ locale }) => ({ props: { ...(await serverSideTranslations(locale, [], config)), }, }); const CreateElection = (props) => { const { t } = useTranslation(); // default value : start at the last hour const now = new Date(); const [title, setTitle] = useState(""); const [candidates, setCandidates] = useState([{ label: "" }, { label: "" }]); const [numGrades, setNumGrades] = useState(5); const [waiting, setWaiting] = useState(false); const [isAdvancedOptionsOpen, setAdvancedOptionsOpen] = useState(false); const [isTimeLimited, setTimeLimited] = useState(false); const [restrictResult, setRestrictResult] = useState(false); const [start, setStart] = useState( new Date(now.getTime() - minutes(now) - seconds(now) - ms(now)) ); const [finish, setFinish] = useState( new Date(start.getTime() + 7 * 24 * 3600 * 1000) ); const [emails, setEmails] = useState([]); // set the title on loading const router = useRouter(); useEffect(() => { if (!router.isReady) return; const { title: urlTitle } = router.query; setTitle(urlTitle || ""); }, [router.isReady]); const handleIsTimeLimited = (event) => { setTimeLimited(event.target.value === "1"); }; const handleRestrictResultCheck = (event) => { setRestrictResult(event.target.value === "1"); }; const toggleAdvancedOptions = () => { setAdvancedOptionsOpen(!isAdvancedOptionsOpen); }; const addCandidate = () => { if (candidates.length < 1000) { candidates.push({ label: "" }); setCandidates(candidates); } }; const checkFields = () => { if (!candidates) { return { ok: false, msg: AT_LEAST_2_CANDIDATES_ERROR }; } let numCandidates = 0; candidates.forEach((c) => { if (c.label !== "") numCandidates += 1; }); if (numCandidates < 2) { return { ok: false, msg: AT_LEAST_2_CANDIDATES_ERROR }; } if (!title || title === "") { return { ok: false, msg: NO_TITLE_ERROR }; } return { ok: true, msg: "OK" }; }; const handleSubmit = () => { const check = checkFields(); if (!check.ok) { toast.error(t(check.msg), { position: toast.POSITION.TOP_CENTER, }); return; } setWaiting(true); createElection( title, candidates.map((c) => c.label).filter((c) => c !== ""), { mails: emails, numGrades, start: start.getTime() / 1000, finish: finish.getTime() / 1000, restrictResult: restrictResult, locale: router.locale.substring(0, 2).toLowerCase(), }, (result) => { if (result.id) { router.push(`/new/confirm/${result.id}`); } else { toast.error(t("Unknown error. Try again please."), { position: toast.POSITION.TOP_CENTER, }); setWaiting(false); } } ); }; const handleSendNotReady = (msg) => { toast.error(t(msg), { position: toast.POSITION.TOP_CENTER, }); }; const check = checkFields(); const grades = translateGrades(t); return ( {waiting ? : ""}

{t("resource.startVote")}


setTitle(e.target.value)} maxLength="250" /> {t("resource.eg")} {t("resource.exampleQuestion")}
- {t("Starting date")} { setStart( new Date( timeMinusDate(start) + new Date(e.target.valueAsNumber).getTime() ) ); }} /> - {t("Ending date")} { setFinish( new Date( timeMinusDate(finish) + new Date(e.target.valueAsNumber).getTime() ) ); }} />

{t("Grades")} {t( "You can select here the number of grades for your election" )}
{t("For example:")}{" "} {" "} {t("5 = Excellent, Very good, Good, Fair, Passable")}
{grades.map((mention, i) => { return ( {mention.label} ); })}

{t("Participants")} { return isEmail(email); // return boolean }} getLabel={(email, index, removeEmail) => { return (
{email} removeEmail(index)} > ×
); }} />
{t( "If you list voters' emails, only them will be able to access the election" )}

{check.ok ? ( ) : ( )}
); }; export default CreateElection;