fix: can not add new candidates after the start of an election

pull/100/head
Pierre-Louis Guhur 1 year ago
parent 1f6902d9ae
commit 85cbba76da

@ -62,7 +62,7 @@ export const ProgressSteps = ({
const { t } = useTranslation();
if (!creationSteps.includes(step)) {
throw Error(`Unknown step {step}`);
throw new Error(`Unknown step {step}`);
}
const stepId = creationSteps.indexOf(step);

@ -17,7 +17,6 @@ interface CandidateProps {
position: number;
className?: string;
defaultAvatar?: any;
editable?: boolean;
[props: string]: any;
}
@ -25,7 +24,6 @@ const CandidateField = ({
position,
className = '',
defaultAvatar = whiteAvatar,
editable = true,
...props
}: CandidateProps) => {
const {t} = useTranslation();
@ -84,28 +82,31 @@ const CandidateField = ({
{candidate.name ? candidate.name : t('admin.add-candidate')}
</div>
</div>
{editable ? (
<div role="button" className="text-end">
{active ? (
<FontAwesomeIcon
icon={faTrashCan}
className="text-black opacity-25"
onClick={() => setModalDel((m) => !m)}
/>
) : (
<FontAwesomeIcon icon={faPlus} onClick={addCandidate} />
)}
</div>
) : null}
<div
{...props}
{...attributes}
{...listeners}
role="button"
className="text-end ms-3"
>
{active ? <VerticalGripDots /> : null}
</div>
{!isCreated(election) && (
<>
<div role="button" className="text-end">
{active ? (
<FontAwesomeIcon
icon={faTrashCan}
className="text-black opacity-25"
onClick={() => setModalDel((m) => !m)}
/>
) : (
<FontAwesomeIcon icon={faPlus} onClick={addCandidate} />
)}
</div>
<div
{...props}
{...attributes}
{...listeners}
role="button"
className="text-end ms-3"
>
{active ? <VerticalGripDots /> : null}
</div>
</>
)}
<CandidateModalSet
toggle={toggleSet}

@ -1,21 +1,21 @@
import { useTranslation } from 'next-i18next';
import { Container, Row, Col } from 'reactstrap';
import { ElectionTypes, useElection } from '@services/ElectionContext';
import { DndContext } from '@dnd-kit/core';
import { arrayMove, SortableContext } from '@dnd-kit/sortable';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPen } from '@fortawesome/free-solid-svg-icons';
import {useTranslation} from 'next-i18next';
import {Container, Row, Col} from 'reactstrap';
import {ElectionTypes, useElection} from '@services/ElectionContext';
import {DndContext} from '@dnd-kit/core';
import {arrayMove, SortableContext} from '@dnd-kit/sortable';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faPen} from '@fortawesome/free-solid-svg-icons';
import CandidateField from './CandidateField';
const CandidatesConfirmField = ({ editable = true }) => {
const { t } = useTranslation();
const CandidatesConfirmField = () => {
const {t} = useTranslation();
const [election, dispatch] = useElection();
const handleDragEnd = (event) => {
/**
* Update the list of grades after dragging an item
*/
const { active, over } = event;
const {active, over} = event;
if (over && over.id && active.id && active.id !== over.id) {
const newCandidates = arrayMove(
@ -45,7 +45,6 @@ const CandidatesConfirmField = ({ editable = true }) => {
</Row>
{election.candidates.map((_, i) => (
<CandidateField
editable={editable}
position={i}
key={i}
className="text-primary m-0"

@ -63,7 +63,7 @@ const submitElection = (
typeof payload.invites === 'undefined' ||
payload.invites.length !== election.emails.length
) {
throw Error('Can not send invite emails');
throw new Error('Can not send invite emails');
}
const urlVotes = payload.invites.map((token: string) =>
getUrl(RouteTypes.VOTE, router, payload.ref, token)

@ -22,7 +22,7 @@ interface GradeInputInterface {
}
const GradeInput = ({gradeId, candidateId}: GradeInputInterface) => {
const [ballot, dispatch] = useBallot();
if (!ballot) {throw Error("Ensure the election is loaded")}
if (!ballot) {throw new Error("Ensure the election is loaded")}
const grade = ballot.election.grades[gradeId];
const numGrades = ballot.election.grades.length;

@ -134,7 +134,7 @@ const HeaderRubbon = ({ token }) => {
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!');
throw new Error('Unexpected number of invites!');
}
const urlVotes = response.invites.map((token: string) =>
getUrl(RouteTypes.VOTE, router, response.ref, token)
@ -284,7 +284,7 @@ const CreateElection = ({ context, token }) => {
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!');
throw new Error('Unexpected number of invites!');
}
const urlVotes = response.invites.map((token: string) =>
getUrl(RouteTypes.VOTE, router, response.ref, token)
@ -342,7 +342,7 @@ const CreateElection = ({ context, token }) => {
<h4>{t('common.the-vote')}</h4>
</Container>
<TitleField defaultName={context.name} />
<CandidatesConfirmField editable={false} />
<CandidatesConfirmField />
</Col>
{!isClosed(election) && (
<Col className="col-lg-9 col-12 mt-3 mt-md-0">

@ -51,7 +51,7 @@ const CreateElectionForm = () => {
/>
);
} else {
throw Error(`Unknown step ${step}`);
throw new Error(`Unknown step ${step}`);
}
if (wait) {

@ -98,7 +98,7 @@ const getNumVotes = (result: ResultInterface) => {
const numVotes = sum(result.meritProfiles[anyCandidateId]);
Object.values(result.meritProfiles).forEach((v) => {
if (sum(v) !== numVotes) {
throw Error(
throw new Error(
'The election does not contain the same number of votes for each candidate'
);
}
@ -435,7 +435,7 @@ const Podium = ({candidates}: PodiumInterface) => {
.forEach((c) => (candidateByRank[c.rank] = c));
if (numBest < 2) {
throw Error('Can not load enough candidates');
throw new Error('Can not load enough candidates');
}
if (numBest === 2) {
@ -501,7 +501,7 @@ const ResultPage = ({
typeof result.candidates === 'undefined' ||
result.candidates.length === 0
) {
throw Error('No candidates were loaded in this election');
throw new Error('No candidates were loaded in this election');
}
const candidateByRank = {};

@ -158,11 +158,17 @@ function electionReducer(
return {...action.value};
}
case ElectionTypes.SET: {
if (isCreated(election) && action.field === 'candidates') {
throw new Error("The election has already started");
}
return {...election, [action.field]: action.value};
}
case ElectionTypes.CANDIDATE_PUSH: {
if (isCreated(election)) {
throw new Error("The election has already started");
}
if (typeof action.value === 'string' && action.value !== 'default') {
throw Error('Unexpected action');
throw new Error('Unexpected action');
}
const candidate =
action.value === 'default' ? {...defaultCandidate} : action.value;
@ -178,7 +184,7 @@ function electionReducer(
}
case ElectionTypes.CANDIDATE_RM: {
if (typeof action.position !== 'number') {
throw Error(`Unexpected candidate position ${action.position}`);
throw new Error(`Unexpected candidate position ${action.position}`);
}
const candidates = [...election.candidates];
candidates.splice(action.position, 1);
@ -186,16 +192,16 @@ function electionReducer(
}
case ElectionTypes.CANDIDATE_SET: {
if (typeof action.position !== 'number') {
throw Error(`Unexpected candidate position ${action.value}`);
throw new Error(`Unexpected candidate position ${action.value}`);
}
if (action.field === 'active') {
throw Error('You are not allowed the set the active flag');
throw new Error('You are not allowed the set the active flag');
}
const candidates = [...election.candidates];
const candidate = candidates[action.position];
candidate[action.field] = action.value;
candidate['active'] = true;
if (candidates.filter((c) => !c.active).length === 0) {
if (!isCreated(election) && candidates.filter((c) => !c.active).length === 0) {
return {
...election,
candidates: [...candidates, {...defaultCandidate}],
@ -209,7 +215,7 @@ function electionReducer(
}
case ElectionTypes.GRADE_RM: {
if (typeof action.position !== 'number') {
throw Error(`Unexpected grade position ${action.position}`);
throw new Error(`Unexpected grade position ${action.position}`);
}
const grades = [...election.grades];
grades.splice(action.position);
@ -217,7 +223,7 @@ function electionReducer(
}
case ElectionTypes.GRADE_SET: {
if (typeof action.position !== 'number') {
throw Error(`Unexpected grade position ${action.position}`);
throw new Error(`Unexpected grade position ${action.position}`);
}
const grades = [...election.grades];
const grade = grades[action.position];

@ -102,7 +102,7 @@ export const createElection = async (
const endpoint = new URL(api.routesServer.setElection, URL_SERVER);
if (!restricted && numVoters > 0) {
throw Error('Set the election as not restricted!');
throw new Error('Set the election as not restricted!');
}
try {
@ -275,7 +275,7 @@ export const castBallot = (
});
} else {
if (!token) {
throw Error('Missing token');
throw new Error('Missing token');
}
return fetch(endpoint.href, {
method: 'PUT',

@ -33,12 +33,12 @@ export const gradeValues = [0, 1, 2, 3, 4, 5, 6, 7];
export const getGradeColor = (gradeIdx: number, numGrades: number): string => {
const extraColors = gradeColors.length - numGrades;
if (extraColors < 0) {
throw Error("More grades than available colors");
throw new Error("More grades than available colors");
}
const startIndex = Math.floor(extraColors / 2);
const colors = gradeColors.slice(startIndex, gradeColors.length - (extraColors - startIndex));
if (colors.length < numGrades) {
throw Error("Issue with the number of colors");
throw new Error("Issue with the number of colors");
}
return colors[colors.length - gradeIdx - 1]
}

@ -31,7 +31,7 @@ export const sendInviteMails = async (
const locale = getLocaleShort(router);
if (!availableLanguages.includes(locale)) {
throw Error(`{locale} is not available for mails`);
throw new Error(`{locale} is not available for mails`);
}
const req = await fetch('/.netlify/functions/send-emails', {

@ -21,7 +21,7 @@ export const getWindowUrl = (): string => {
export const displayRef = (ref: string): string => {
const cl = ref.replaceAll('-', '');
if (cl.length !== 10) {
throw Error('Unexpected election ref');
throw new Error('Unexpected election ref');
}
return `${cl.substring(0, 3)}-${cl.substring(3, 6)}-${cl.substring(6)}`;

Loading…
Cancel
Save