fix: results were limited to 3 candidates

pull/100/head
Pierre-Louis Guhur 1 year ago
parent 481f5fcaf9
commit c67f317563

@ -1,11 +1,11 @@
import { useState } from 'react'; import {useState} from 'react';
import Head from 'next/head'; import Head from 'next/head';
import Image from 'next/image'; import Image from 'next/image';
import { useTranslation } from 'next-i18next'; import {useTranslation} from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'; import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
import Link from 'next/link'; import Link from 'next/link';
import { Container, Collapse, Card, CardHeader, CardBody } from 'reactstrap'; import {Container, Collapse, Card, CardHeader, CardBody} from 'reactstrap';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import { import {
faArrowRight, faArrowRight,
faChevronDown, faChevronDown,
@ -18,25 +18,25 @@ import CSVLink from '@components/CSVLink';
import Logo from '@components/Logo'; import Logo from '@components/Logo';
import MeritProfile from '@components/MeritProfile'; import MeritProfile from '@components/MeritProfile';
import Button from '@components/Button'; import Button from '@components/Button';
import { getResults } from '@services/api'; import {getResults} from '@services/api';
import { import {
GradeResultInterface, GradeResultInterface,
ResultInterface, ResultInterface,
MeritProfileInterface, MeritProfileInterface,
CandidateResultInterface, CandidateResultInterface,
} from '@services/type'; } from '@services/type';
import { getUrl, RouteTypes } from '@services/routes'; import {getUrl, RouteTypes} from '@services/routes';
import { displayRef, getLocaleShort } from '@services/utils'; import {displayRef, getLocaleShort} from '@services/utils';
import { getMajorityGrade } from '@services/majorityJudgment'; import {getMajorityGrade} from '@services/majorityJudgment';
import avatarBlue from '../../../public/avatarBlue.svg'; import avatarBlue from '../../../public/avatarBlue.svg';
import calendar from '../../../public/calendar.svg'; import calendar from '../../../public/calendar.svg';
import arrowUpload from '../../../public/arrowUpload.svg'; import arrowUpload from '../../../public/arrowUpload.svg';
import arrowLink from '../../../public/arrowL.svg'; import arrowLink from '../../../public/arrowL.svg';
import { getGradeColor } from '@services/grades'; import {getGradeColor} from '@services/grades';
import { useRouter } from 'next/router'; import {useRouter} from 'next/router';
export async function getServerSideProps({ query, locale }) { export async function getServerSideProps({query, locale}) {
const { pid, tid: token } = query; const {pid, tid: token} = query;
const electionRef = pid.replaceAll('-', ''); const electionRef = pid.replaceAll('-', '');
const [payload, translations] = await Promise.all([ const [payload, translations] = await Promise.all([
@ -45,7 +45,7 @@ export async function getServerSideProps({ query, locale }) {
]); ]);
if ('msg' in payload) { if ('msg' in payload) {
return { props: { err: payload.msg, electionRef, ...translations } }; return {props: {err: {message: payload.msg}, electionRef, ...translations}};
} }
const numGrades = payload.grades.length; const numGrades = payload.grades.length;
@ -53,7 +53,7 @@ export async function getServerSideProps({ query, locale }) {
...g, ...g,
color: getGradeColor(i, numGrades), color: getGradeColor(i, numGrades),
})); }));
const gradesByValue: { [key: number]: GradeResultInterface } = {}; const gradesByValue: {[key: number]: GradeResultInterface} = {};
grades.forEach((g) => (gradesByValue[g.value] = g)); grades.forEach((g) => (gradesByValue[g.value] = g));
const result: ResultInterface = { const result: ResultInterface = {
@ -106,8 +106,8 @@ const getNumVotes = (result: ResultInterface) => {
return numVotes; return numVotes;
}; };
const WillClose = ({ delay }) => { const WillClose = ({delay}) => {
const { t } = useTranslation(); const {t} = useTranslation();
if (delay < 365) { if (delay < 365) {
return <div>{t('result.closed')}</div>; return <div>{t('result.closed')}</div>;
} else if (delay < 0) { } else if (delay < 0) {
@ -126,8 +126,8 @@ const WillClose = ({ delay }) => {
interface ResultBanner { interface ResultBanner {
result: ResultInterface; result: ResultInterface;
} }
const ResultBanner = ({ result }) => { const ResultBanner = ({result}) => {
const { t } = useTranslation(); const {t} = useTranslation();
const router = useRouter(); const router = useRouter();
const dateEnd = new Date(result.dateEnd); const dateEnd = new Date(result.dateEnd);
@ -207,16 +207,16 @@ const ResultBanner = ({ result }) => {
); );
}; };
const Downloader = ({ result, children, ...rest }) => { const Downloader = ({result, children, ...rest}) => {
const values = result.grades.map((v) => v.value).sort(); const values = result.grades.map((v) => v.value).sort();
const data = result.candidates.map((c) => { const data = result.candidates.map((c) => {
const grades = {}; const grades = {};
result.grades.forEach( result.grades.forEach(
(g) => (g) =>
(grades[g.name] = (grades[g.name] =
g.value in c.meritProfile ? c.meritProfile[g.value].toString() : '0') g.value in c.meritProfile ? c.meritProfile[g.value].toString() : '0')
); );
return { name: c.name, ...grades }; return {name: c.name, ...grades};
}); });
return ( return (
@ -230,8 +230,8 @@ const Downloader = ({ result, children, ...rest }) => {
); );
}; };
const BottomButtonsMobile = ({ result }) => { const BottomButtonsMobile = ({result}) => {
const { t } = useTranslation(); const {t} = useTranslation();
const router = useRouter(); const router = useRouter();
const url = getUrl(RouteTypes.RESULTS, router, result.ref); const url = getUrl(RouteTypes.RESULTS, router, result.ref);
@ -276,8 +276,8 @@ interface TitleBannerInterface {
token?: string; token?: string;
} }
const TitleBanner = ({ name, electionRef, token }: TitleBannerInterface) => { const TitleBanner = ({name, electionRef, token}: TitleBannerInterface) => {
const { t } = useTranslation(); const {t} = useTranslation();
const router = useRouter(); const router = useRouter();
const locale = getLocaleShort(router); const locale = getLocaleShort(router);
@ -331,7 +331,7 @@ interface ButtonGradeResultInterface {
grade: GradeResultInterface; grade: GradeResultInterface;
} }
const ButtonGrade = ({ grade }: ButtonGradeResultInterface) => { const ButtonGrade = ({grade}: ButtonGradeResultInterface) => {
const style = { const style = {
color: 'white', color: 'white',
backgroundColor: grade.color, backgroundColor: grade.color,
@ -351,7 +351,7 @@ interface CandidateRankedInterface {
candidate: CandidateResultInterface; candidate: CandidateResultInterface;
} }
const CandidateRanked = ({ candidate }: CandidateRankedInterface) => { const CandidateRanked = ({candidate}: CandidateRankedInterface) => {
const isFirst = candidate.rank == 1; const isFirst = candidate.rank == 1;
return ( return (
<div className="m-3 d-flex flex-column justify-content-end align-items-center candidate_rank fw-bold"> <div className="m-3 d-flex flex-column justify-content-end align-items-center candidate_rank fw-bold">
@ -377,8 +377,8 @@ interface CandidateCardInterface {
grades: Array<GradeResultInterface>; grades: Array<GradeResultInterface>;
} }
const CandidateCard = ({ candidate, grades }: CandidateCardInterface) => { const CandidateCard = ({candidate, grades}: CandidateCardInterface) => {
const { t } = useTranslation(); const {t} = useTranslation();
const [collapse, setCollapse] = useState(true); const [collapse, setCollapse] = useState(true);
return ( return (
@ -424,8 +424,8 @@ interface PodiumInterface {
candidates: Array<CandidateResultInterface>; candidates: Array<CandidateResultInterface>;
} }
const Podium = ({ candidates }: PodiumInterface) => { const Podium = ({candidates}: PodiumInterface) => {
const { t } = useTranslation(); const {t} = useTranslation();
// get best candidates // get best candidates
const numBest = Math.min(3, candidates.length); const numBest = Math.min(3, candidates.length);
@ -472,7 +472,7 @@ const ResultPage = ({
err, err,
electionRef, electionRef,
}: ResultPageInterface) => { }: ResultPageInterface) => {
const { t } = useTranslation(); const {t} = useTranslation();
const router = useRouter(); const router = useRouter();
if (err && err.message.startsWith('No votes')) { if (err && err.message.startsWith('No votes')) {
@ -506,7 +506,6 @@ const ResultPage = ({
const candidateByRank = {}; const candidateByRank = {};
result.candidates result.candidates
.filter((c) => c.rank < 4)
.forEach((c) => (candidateByRank[c.rank] = c)); .forEach((c) => (candidateByRank[c.rank] = c));
return ( return (
@ -522,7 +521,7 @@ const ResultPage = ({
<Podium candidates={result.candidates} /> <Podium candidates={result.candidates} />
<Container <Container
style={{ maxWidth: '750px' }} style={{maxWidth: '750px'}}
className="mt-5 h-100 d-flex flex-fill flex-column justify-content-between" className="mt-5 h-100 d-flex flex-fill flex-column justify-content-between"
> >
<div> <div>

@ -1,5 +1,5 @@
import { Candidate, Grade, Vote } from './type'; import {Candidate, Grade, Vote} from './type';
import { URL_SERVER } from './constants'; import {URL_SERVER} from './constants';
export const api = { export const api = {
routesServer: { routesServer: {
@ -67,8 +67,8 @@ export interface ElectionUpdatedPayload extends ElectionPayload {
export interface ResultsPayload extends ElectionPayload { export interface ResultsPayload extends ElectionPayload {
status: number; status: number;
ranking: { [key: string]: number }; ranking: {[key: string]: number};
merit_profile: { [key: number]: Array<number> }; merit_profile: {[key: number]: Array<number>};
} }
export interface VotePayload { export interface VotePayload {
@ -186,13 +186,13 @@ export const updateElection = async (
}); });
if (!req.ok || req.status !== 200) { if (!req.ok || req.status !== 200) {
const payload = await req.json(); const payload = await req.json();
return { status: req.status, msg: payload }; return {status: req.status, msg: payload};
} }
const payload = await req.json(); const payload = await req.json();
return { status: 200, ...payload }; return {status: 200, ...payload};
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return { status: 400, msg: 'Unknown API error' }; return {status: 400, msg: `Unknown API error: ${e}`};
} }
}; };
@ -212,13 +212,13 @@ export const getResults = async (
const response = await fetch(endpoint.href); const response = await fetch(endpoint.href);
if (response.status != 200) { if (response.status != 200) {
const payload = await response.json(); const payload = await response.json();
return { status: response.status, msg: payload }; return {status: response.status, msg: payload};
} }
const payload = await response.json(); const payload = await response.json();
return { ...payload, status: response.status }; return {...payload, status: response.status};
} catch (error) { } catch (error) {
console.error(error); console.error(error);
return { status: 400, msg: 'Unknown API error' }; return {status: 400, msg: `Unknown API error: ${error}`};
} }
}; };
@ -239,12 +239,12 @@ export const getElection = async (
if (response.status != 200) { if (response.status != 200) {
const payload = await response.json(); const payload = await response.json();
return { status: response.status, msg: payload }; return {status: response.status, msg: payload};
} }
const payload = await response.json(); const payload = await response.json();
return { ...payload, status: response.status }; return {...payload, status: response.status};
} catch (error) { } catch (error) {
return { status: 400, msg: 'Unknown API error' }; return {status: 400, msg: 'Unknown API error'};
} }
}; };
@ -270,7 +270,7 @@ export const castBallot = (
if (!election.restricted) { if (!election.restricted) {
return fetch(endpoint.href, { return fetch(endpoint.href, {
method: 'POST', method: 'POST',
headers: { 'Content-Type': 'application/json' }, headers: {'Content-Type': 'application/json'},
body: JSON.stringify(payload), body: JSON.stringify(payload),
}); });
} else { } else {

Loading…
Cancel
Save