import { useEffect, useState } from 'react'; import { useRouter } from 'next/router'; import Link from 'next/link'; import { useTranslation } from 'next-i18next'; import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import { Container, Row, Col } from 'reactstrap'; import { faArrowRight } from '@fortawesome/free-solid-svg-icons'; import { getElection, updateElection } from '@services/api'; import { ElectionContextInterface, ElectionProvider, ElectionTypes, useElection, isClosed, canViewResults, checkName, hasEnoughGrades, hasEnoughCandidates, canBeFinished, } from '@services/ElectionContext'; import { CandidateItem, GradeItem } from '@services/type'; import { gradeColors } from '@services/grades'; import TitleField from '@components/admin/Title'; import Button from '@components/Button'; import AccessResults from '@components/admin/AccessResults'; import CandidatesConfirmField from '@components/admin/CandidatesConfirmField'; import LimitDate from '@components/admin/LimitDate'; import Grades from '@components/admin/Grades'; import Order from '@components/admin/Order'; import Private from '@components/admin/Private'; import Blur from '@components/Blur'; import { getUrlResults, getUrlVote, RESULTS, VOTE } from '@services/routes'; import { sendInviteMails } from '@services/mail'; import { AppTypes, useAppContext } from '@services/context'; export async function getServerSideProps({ query, locale }) { const { pid, tid: token } = query; const electionRef = pid.replaceAll('-', ''); const [payload, translations] = await Promise.all([ getElection(electionRef), serverSideTranslations(locale, ['resource']), ]); if ('msg' in payload) { return { props: { err: payload.msg, ...translations } }; } const grades = payload.grades.map((g, i) => ({ ...g, active: true })); const candidates: Array = payload.candidates.map((c) => ({ ...c, active: true, })); const description = JSON.parse(payload.description); const randomOrder = description['randomOrder']; const context: ElectionContextInterface = { name: payload.name, description: description['description'], ref: payload.ref, dateStart: payload.date_start, dateEnd: payload.date_end, hideResults: payload.hide_results, forceClose: payload.force_close, restricted: payload.restricted, randomOrder, emails: [], grades, candidates, }; return { props: { context, token: token || '', ...translations, }, }; } const Spinner = () => { return (
Loading...
); }; const HeaderRubbon = () => { const { t } = useTranslation(); const [election, dispatch] = useElection(); const [_, dispatchApp] = useAppContext(); const router = useRouter(); const [waiting, setWaiting] = useState(false); const handleClosing = async () => { setWaiting(true); dispatch({ type: ElectionTypes.SET, field: 'forceClose', value: true, }); const candidates = election.candidates .filter((c) => c.active) .map((c: CandidateItem) => ({ name: c.name, description: c.description, image: c.image, })); const grades = election.grades .filter((c) => c.active) .map((g: GradeItem, i: number) => ({ name: g.name, value: i })); setWaiting(true); const response = await updateElection( election.ref, election.name, candidates, grades, election.description, election.emails.length, election.hideResults, true, election.restricted, election.randomOrder ); if (response.status === 200 && 'ref' in response) { if (election.restricted && election.emails.length > 0) { if (election.emails.length !== response.invites.length) { throw Error('Unexpected number of invites!'); } const urlVotes = response.invites.map((token: string) => getUrlVote(response.ref, token) ); const urlResult = getUrlResults(response.ref); await sendInviteMails( election.emails, election.name, urlVotes, urlResult, router ); } setWaiting(false); dispatchApp({ type: AppTypes.TOAST_ADD, status: 'success', message: t('success.election-closed'), }); } }; return (
{t('admin.admin-title')}
{election.restricted ? null : ( )} {canViewResults(election) ? ( ) : null} {isClosed(election) ? null : ( )}
); }; const CreateElection = ({ context, token }) => { const { t } = useTranslation(); const [election, dispatch] = useElection(); const [_, dispatchApp] = useAppContext(); const router = useRouter(); const [waiting, setWaiting] = useState(false); useEffect(() => { dispatch({ type: ElectionTypes.RESET, value: context }); }, []); const handleSubmit = async () => { if (!checkName(election)) { dispatchApp({ type: AppTypes.TOAST_ADD, status: 'error', message: t('error.uncorrect-name'), }); return; } if (!hasEnoughGrades(election)) { dispatchApp({ type: AppTypes.TOAST_ADD, status: 'error', message: t('error.not-enough-grades'), }); return; } if (!hasEnoughCandidates(election)) { dispatchApp({ type: AppTypes.TOAST_ADD, status: 'error', message: t('error.not-enough-candidates'), }); return; } if (!canBeFinished(election)) { dispatchApp({ type: AppTypes.TOAST_ADD, status: 'error', message: t('error.cant-be-finished'), }); return; } const candidates = election.candidates .filter((c) => c.active) .map((c: CandidateItem) => ({ name: c.name, description: c.description, image: c.image, })); const grades = election.grades .filter((c) => c.active) .map((g: GradeItem, i: number) => ({ name: g.name, value: i })); setWaiting(true); const response = await updateElection( election.ref, election.name, candidates, grades, election.description, election.emails.length, election.hideResults, true, election.restricted, election.randomOrder ); if (response.status === 200 && 'ref' in response) { if (election.restricted && election.emails.length > 0) { if (election.emails.length !== response.invites.length) { throw Error('Unexpected number of invites!'); } const urlVotes = response.invites.map((token: string) => getUrlVote(response.ref, token) ); const urlResult = getUrlResults(response.ref); await sendInviteMails( election.emails, election.name, urlVotes, urlResult, router ); } setWaiting(false); dispatchApp({ type: AppTypes.TOAST_ADD, status: 'success', message: t('success.election-updated'), }); } }; const numCandidates = election.candidates.filter( (c) => c.active && c.name != '' ).length; const numGrades = election.grades.filter( (g) => g.active && g.name != '' ).length; const disabled = !election.name || election.name == '' || numCandidates < 2 || numGrades < 2 || numGrades > gradeColors.length; return ( <>

{t('admin.confirm-title')}

{t('common.the-vote')}

{t('common.the-params')}

); }; const CreateElectionProviding = ({ children, context, token }) => ( ); export default CreateElectionProviding;