fix: emails

pull/89/head
Pierre-Louis Guhur 1 year ago
parent 7055245ecd
commit c8040922f9

@ -93,7 +93,7 @@ const CandidatesField = ({ onSubmit }) => {
<>
<Container onClick={toggleModalTitle} className="candidate mt-5">
<h4 className="mb-4">{t('admin.confirm-question')}</h4>
<div className="d-flex justify-content-between border border-dashed border-2 border-light border-opacity-25 px-4 py-3 mx-2">
<div className="d-flex justify-content-between border border-dashed border-2 border-light border-opacity-25 px-4 py-3 mx-2 mx-md-0">
<h5 className="m-0 text-white">{election.name}</h5>
<FontAwesomeIcon icon={faPen} />
</div>

@ -64,7 +64,6 @@ const Grades = () => {
});
}
}, []);
console.log('GRADES', grades);
const handleDragEnd = (event) => {
/**

@ -81,7 +81,7 @@
<tr>
<th scope="col" style="vertical-align: top; padding: 40px 10px 40px 10px;">
<a href="https://mieuxvoter.fr/" target="_blank" rel="noopener noreferrer">
<img alt="Logo" src="https://mieuxvoter.fr/img/logo.svg" width="40" height="40" style="display: block; margin: 0px auto 0px auto; width: 50%; max-width: 250px; min-width: 40px; height: auto; font-family: 'Lato', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;" border="0">
<img alt="Logo" src="https://app.mieuxvoter.fr/logos/logo.png" width="40" height="40" style="display: block; margin: 0px auto 0px auto; width: 50%; max-width: 250px; min-width: 40px; height: auto; font-family: 'Lato', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;" border="0">
</a>
</th>
</tr>
@ -135,7 +135,7 @@
<!-- BLOCK THANKS -->
<tr>
<th scope="col" style="background-color: #ffffff; padding: 0px 30px 40px 30px; border-radius: 0px 0px 4px 4px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 18px; font-weight: 400; line-height: 25px;" >
<p style="margin: 0; text-align: left;">{{#i18n 'email.bye'}}{{/i18n}},<br>{{#i18n 'common.better-vote'}}{{/i18n}}</p>
<p style="margin: 0; text-align: left;">{{#i18n 'email.bye'}}{{/i18n}},<br>{{#i18n 'resource:common.better-vote'}}{{/i18n}}</p>
</th>
</tr>
</table>
@ -155,7 +155,7 @@
</p>
<p style="margin: 0;"> <strong>
<a href="https://www.paypal.com/donate/?hosted_button_id=QD6U4D323WV4S" target="_blank" style="color: #111111;" rel="noopener noreferrer">
{{#i18n 'common.support-us'}}{{/i18n}}
{{#i18n 'resource:common.support-us'}}{{/i18n}}
</a></strong>
</p>
</th>
@ -179,7 +179,7 @@
<!-- ADDRESS -->
<tr>
<th scope="col" style="background-color: #f4f4f4; padding: 0px 30px 30px 30px; color: #666666; font-family: 'Lato', Helvetica, Arial, sans-serif; font-size: 14px; font-weight: 400; line-height: 18px;" >
<p style="margin: 0;">{{#i18n common.better-vote }}{{/i18n}} - <a "mailto:{{ from_email_address }}">{{ from_email_address }}</a></p>
<p style="margin: 0;">{{#i18n resource:common.better-vote }}{{/i18n}} - <a "mailto:{{ from_email_address }}">{{ from_email_address }}</a></p>
</th>
</tr>
</table>

@ -12,4 +12,4 @@
{{#i18n 'email.bye'}}{{/i18n}}
{{#i18n 'common.better-vote'}}{{/i18n}}
{{#i18n 'resource:common.better-vote'}}{{/i18n}}

@ -81,7 +81,7 @@
<tr>
<th scope="col" style="vertical-align: top; padding: 40px 10px 40px 10px;">
<a href="https://mieuxvoter.fr/" target="_blank" rel="noopener noreferrer">
<img alt="Logo" src="https://mieuxvoter.fr/img/logo.svg" width="40" height="40" style="display: block; margin: 0px auto 0px auto; width: 50%; max-width: 250px; min-width: 40px; height: auto; font-family: 'Lato', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;" border="0">
<img alt="Logo" src="https://app.mieuxvoter.fr/logos/logo.png" width="40" height="40" style="display: block; margin: 0px auto 0px auto; width: 50%; max-width: 250px; min-width: 40px; height: auto; font-family: 'Lato', Helvetica, Arial, sans-serif; color: #ffffff; font-size: 18px;" border="0">
</a>
</th>
</tr>

@ -16,4 +16,4 @@
{{#i18n 'email.bye'}}{{/i18n}}
{{#i18n 'common.better-vote'}}{{/i18n}}
{{#i18n 'resource:common.better-vote'}}{{/i18n}}

@ -1,14 +1,25 @@
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 { 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';
@ -18,33 +29,35 @@ 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';
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("-", "");
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"]),
serverSideTranslations(locale, ['resource']),
]);
if ("msg" in payload) {
return {props: {err: payload.msg, ...translations}};
if ('msg' in payload) {
return { props: { err: payload.msg, ...translations } };
}
const grades = payload.grades.map((g, i) => ({...g, active: true}));
const grades = payload.grades.map((g, i) => ({ ...g, active: true }));
const candidates: Array<CandidateItem> = payload.candidates.map(c => ({...c, active: true}))
const description = JSON.parse(payload.description)
const randomOrder = description["randomOrder"]
const candidates: Array<CandidateItem> = 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"],
description: description['description'],
ref: payload.ref,
dateStart: payload.date_start,
dateEnd: payload.date_end,
@ -54,45 +67,51 @@ export async function getServerSideProps({query, locale}) {
randomOrder,
emails: [],
grades,
candidates
}
candidates,
};
return {
props: {
context,
token: token || "",
token: token || '',
...translations,
},
};
}
const Spinner = () => {
return (
<div className="spinner-border text-light" role="status">
<span className="visually-hidden">Loading...</span>
</div>
)
}
);
};
const HeaderRubbon = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const [election, dispatch] = useElection();
const [_, dispatchApp] = useAppContext();
const router = useRouter();
const [waiting, setWaiting] = useState(false);
const handleClosing = async () => {
setWaiting(true)
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)
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,
@ -103,130 +122,141 @@ const HeaderRubbon = () => {
election.hideResults,
true,
election.restricted,
election.randomOrder,
election.randomOrder
);
if (response.status === 200 && "ref" in response) {
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 Error('Unexpected number of invites!');
}
const urlVotes = response.invites.map((token: string) => getUrlVote(response.ref, token));
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,
router
);
}
setWaiting(false)
setWaiting(false);
dispatchApp({
type: AppTypes.TOAST_ADD,
status: "success",
message: t("success.election-closed")
})
}
status: 'success',
message: t('success.election-closed'),
});
}
};
return <div className="w-100 p-4 bg-primary text-white d-flex justify-content-between align-items-center">
return (
<div className="w-100 p-4 bg-primary text-white d-flex justify-content-between align-items-center">
<h5>{t('admin.admin-title')}</h5>
<div className="d-flex">
{election.restricted ? null :
{election.restricted ? null : (
<Link href={`${VOTE}/${election.ref}`}>
<Button icon={faArrowRight}
<Button
icon={faArrowRight}
color="primary"
className="me-3"
style={{border: "2px solid rgba(255, 255, 255, 0.4)"}}
position="right">
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
position="right"
>
{t('admin.go-to-vote')}
</Button>
</Link>
}
)}
{canViewResults(election) ?
{canViewResults(election) ? (
<Link href={`${RESULTS}/${election.ref}`}>
<Button icon={faArrowRight}
<Button
icon={faArrowRight}
color="primary"
className="me-3"
style={{border: "2px solid rgba(255, 255, 255, 0.4)"}}
position="right">
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
position="right"
>
{t('admin.go-to-result')}
</Button>
</Link>
: null}
) : null}
{isClosed(election) ? null :
{isClosed(election) ? null : (
<Button
className="me-3 btn_closing"
style={{border: "2px solid rgba(255, 255, 255, 0.4)"}}
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
onClick={handleClosing}
position="right">
{waiting ?
<Spinner />
:
t('admin.close-election')
}
position="right"
>
{waiting ? <Spinner /> : t('admin.close-election')}
</Button>
}
)}
</div>
</div>
);
};
</div >
}
const CreateElection = ({context, token}) => {
const {t} = useTranslation();
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})
}, [])
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
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
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
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
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 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,
@ -238,43 +268,51 @@ const CreateElection = ({context, token}) => {
election.hideResults,
true,
election.restricted,
election.randomOrder,
election.randomOrder
);
if (response.status === 200 && "ref" in response) {
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 Error('Unexpected number of invites!');
}
const urlVotes = response.invites.map((token: string) => getUrlVote(response.ref, token));
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,
router
);
}
setWaiting(false)
setWaiting(false);
dispatchApp({
type: AppTypes.TOAST_ADD,
status: "success",
message: t("success.election-updated")
})
}
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 == "" ||
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
)
numGrades < 2 ||
numGrades > gradeColors.length;
return (
<><HeaderRubbon />
<>
<HeaderRubbon />
<Container
fluid="xl"
className="my-5 flex-column d-flex justify-content-center"
@ -319,12 +357,11 @@ const CreateElection = ({context, token}) => {
);
};
const CreateElectionProviding = ({children, context, token}) => (
const CreateElectionProviding = ({ children, context, token }) => (
<ElectionProvider>
<Blur />
<CreateElection context={context} token={token} />
</ElectionProvider>
)
);
export default CreateElectionProviding;

@ -1,50 +1,63 @@
import {useEffect, useState} from 'react';
import { useEffect, useState } from 'react';
import Head from 'next/head';
import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
import {useTranslation} from 'next-i18next';
import {Container} from 'reactstrap';
import {faCheck} from '@fortawesome/free-solid-svg-icons';
import BallotDesktop from '@components/ballot/BallotDesktop'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useTranslation } from 'next-i18next';
import { Container } from 'reactstrap';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
import BallotDesktop from '@components/ballot/BallotDesktop';
import Button from '@components/Button';
import BallotMobile from '@components/ballot/BallotMobile'
import Blur from '@components/Blur'
import {getElection, castBallot, ElectionPayload, BallotPayload, ErrorPayload} from '@services/api';
import {useBallot, BallotTypes, BallotProvider} from '@services/BallotContext';
import {ENDED_VOTE} from '@services/routes';
import {isEnded} from '@services/utils';
import BallotMobile from '@components/ballot/BallotMobile';
import Blur from '@components/Blur';
import {
getElection,
castBallot,
ElectionPayload,
BallotPayload,
ErrorPayload,
} from '@services/api';
import {
useBallot,
BallotTypes,
BallotProvider,
} from '@services/BallotContext';
import { ENDED_VOTE } from '@services/routes';
import { isEnded } from '@services/utils';
import WaitingBallot from '@components/WaitingBallot';
import PatternedBackground from '@components/PatternedBackground';
const shuffle = (array) => array.sort(() => Math.random() - 0.5);
export async function getServerSideProps({query: {pid, tid}, locale}) {
export async function getServerSideProps({ query: { pid, tid }, locale }) {
if (!pid) {
return {notFound: true}
return { notFound: true };
}
const electionRef = pid.replaceAll("-", "");
const electionRef = pid.replaceAll('-', '');
const [election, translations] = await Promise.all([
getElection(electionRef),
serverSideTranslations(locale, ['resource']),
]);
if ("msg" in election) {
return {notFound: true}
if ('msg' in election) {
return { notFound: true };
}
if (isEnded(election.date_end)) {
return {
redirect: {
destination: `${ENDED_VOTE}/${pid}/${tid || ""}`,
permanent: false
}
}
destination: `${ENDED_VOTE}/${pid}/${tid || ''}`,
permanent: false,
},
};
}
if (!election || !election.candidates || !Array.isArray(election.candidates)) {
if (
!election ||
!election.candidates ||
!Array.isArray(election.candidates)
) {
console.log(election);
return {notFound: true}
return { notFound: true };
}
const description = JSON.parse(election.description);
@ -62,13 +75,13 @@ export async function getServerSideProps({query: {pid, tid}, locale}) {
};
}
const ButtonSubmit = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const [ballot, dispatch] = useBallot();
const disabled = ballot.votes.length !== ballot.election.candidates.length;
return (<Container className="my-5 d-md-flex d-grid justify-content-md-center">
return (
<Container className="my-5 d-md-flex d-grid justify-content-md-center">
<Button
outline={true}
color="secondary"
@ -81,17 +94,16 @@ const ButtonSubmit = () => {
{t('vote.submit')}
</Button>
</Container>
)
}
);
};
interface VoteInterface {
election: ElectionPayload;
err: string;
token?: string;
}
const VoteBallot = ({election, token}: VoteInterface) => {
const {t} = useTranslation();
const VoteBallot = ({ election, token }: VoteInterface) => {
const { t } = useTranslation();
const [ballot, dispatch] = useBallot();
@ -111,9 +123,11 @@ const VoteBallot = ({election, token}: VoteInterface) => {
}
if (voting) {
return <PatternedBackground>
return (
<PatternedBackground>
<WaitingBallot ballot={payload} error={error} />
</PatternedBackground>
);
}
const handleSubmit = async (event) => {
@ -121,27 +135,27 @@ const VoteBallot = ({election, token}: VoteInterface) => {
setVoting(true);
try {
const res = await castBallot(
ballot.votes,
ballot.election,
token)
const res = await castBallot(ballot.votes, ballot.election, token);
if (res.status !== 200) {
console.error(res);
const msg = await res.json();
setError(msg)
}
else {
setError(msg);
} else {
const msg = await res.json();
setPayload(msg)
setPayload(msg);
}
} catch (err) {
console.error(err);
setError(err.message)
setError(err.message);
}
};
return (
<form className="w-100 h-100" onSubmit={handleSubmit} autoComplete="off">
<form
className="w-100 flex-fill d-flex align-items-center"
onSubmit={handleSubmit}
autoComplete="off"
>
<Head>
<title>{election.name}</title>
@ -159,16 +173,16 @@ const VoteBallot = ({election, token}: VoteInterface) => {
<BallotMobile />
<ButtonSubmit />
</div>
</form >
</form>
);
};
const Ballot = (props) => {
return (<BallotProvider>
return (
<BallotProvider>
<VoteBallot {...props} />
</BallotProvider>)
}
</BallotProvider>
);
};
export default Ballot;

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

@ -1,8 +1,7 @@
import {Candidate, Grade, Vote} from './type';
import { Candidate, Grade, Vote } from './type';
export const api = {
urlServer:
process.env.NEXT_PUBLIC_SERVER_URL || 'https://api.mieuxvoter.fr/',
urlServer: process.env.NEXT_PUBLIC_SERVER_URL || 'https://api.mieuxvoter.fr/',
feedbackForm:
process.env.NEXT_PUBLIC_FEEDBACK_FORM ||
'https://docs.google.com/forms/d/e/1FAIpQLScuTsYeBXOSJAGSE_AFraFV7T2arEYua7UCM4NRBSCQQfRB6A/viewform',
@ -14,7 +13,6 @@ export const api = {
},
};
export interface GradePayload {
name: string;
description: string;
@ -22,7 +20,6 @@ export interface GradePayload {
value: number;
}
export interface CandidatePayload {
name: string;
description: string;
@ -30,7 +27,6 @@ export interface CandidatePayload {
image: string;
}
export interface ErrorMessage {
loc: Array<string>;
msg: string;
@ -72,14 +68,12 @@ export interface ElectionUpdatedPayload extends ElectionPayload {
status?: number;
}
export interface ResultsPayload extends ElectionPayload {
status: number;
ranking: {[key: string]: number};
merit_profile: {[key: number]: Array<number>};
ranking: { [key: string]: number };
merit_profile: { [key: number]: Array<number> };
}
export interface VotePayload {
id: string;
candidate: CandidatePayload;
@ -111,7 +105,7 @@ export const createElection = async (
const endpoint = new URL(api.routesServer.setElection, api.urlServer);
if (!restricted && numVoters > 0) {
throw Error("Set the election as not restricted!");
throw Error('Set the election as not restricted!');
}
try {
@ -124,7 +118,7 @@ export const createElection = async (
name,
description: JSON.stringify({
description: description,
randomOrder: randomOrder
randomOrder: randomOrder,
}),
candidates,
grades,
@ -133,7 +127,7 @@ export const createElection = async (
force_close: forceClose,
restricted,
}),
})
});
if (req.ok && req.status === 200) {
if (successCallback) {
const payload = await req.json();
@ -143,19 +137,16 @@ export const createElection = async (
} else if (failureCallback) {
try {
const payload = await req.json();
failureCallback(payload)
failureCallback(payload);
} catch (e) {
failureCallback(req.statusText)
}
failureCallback(req.statusText);
}
}
catch (e) {
} catch (e) {
return failureCallback && failureCallback(e);
}
};
export const updateElection = async (
ref: string,
name: string,
@ -166,17 +157,13 @@ export const updateElection = async (
hideResults: boolean,
forceClose: boolean,
restricted: boolean,
randomOrder: boolean,
randomOrder: boolean
): Promise<ElectionUpdatedPayload | HTTPPayload> => {
/**
* Create an election from its title, its candidates and a bunch of options
*/
const endpoint = new URL(api.routesServer.setElection, api.urlServer);
if (!restricted && numVoters > 0) {
throw Error("Set the election as not restricted!");
}
try {
const req = await fetch(endpoint.href, {
method: 'POST',
@ -188,7 +175,7 @@ export const updateElection = async (
name,
description: JSON.stringify({
description: description,
randomOrder: randomOrder
randomOrder: randomOrder,
}),
candidates,
grades,
@ -197,23 +184,22 @@ export const updateElection = async (
force_close: forceClose,
restricted,
}),
})
});
if (!req.ok || req.status !== 200) {
const payload = await req.json();
return {status: req.status, msg: payload};
return { status: req.status, msg: payload };
}
const payload = await req.json();
return {status: 200, ...payload}
}
catch (e) {
console.error(e)
return {status: 400, msg: "Unknown API error"}
return { status: 200, ...payload };
} catch (e) {
console.error(e);
return { status: 400, msg: 'Unknown API error' };
}
};
export const getResults = async (pid: string): Promise<ResultsPayload | HTTPPayload> => {
export const getResults = async (
pid: string
): Promise<ResultsPayload | HTTPPayload> => {
/**
* Fetch results from external API
*/
@ -224,25 +210,29 @@ export const getResults = async (pid: string): Promise<ResultsPayload | HTTPPayl
);
try {
const response = await fetch(endpoint.href)
const response = await fetch(endpoint.href);
if (response.status != 200) {
const payload = await response.json();
return {status: response.status, msg: payload};
return { status: response.status, msg: payload };
}
const payload = await response.json()
return {...payload, status: response.status};
const payload = await response.json();
return { ...payload, status: response.status };
} catch (error) {
console.error(error)
return {status: 400, msg: "Unknown API error"}
console.error(error);
return { status: 400, msg: 'Unknown API error' };
}
};
export const getElection = async (pid: string): Promise<ElectionPayload | HTTPPayload> => {
export const getElection = async (
pid: string
): Promise<ElectionPayload | HTTPPayload> => {
/**
* Fetch data from external API
*/
const path = api.routesServer.getElection.replace(new RegExp(':slug', 'g'), pid);
const path = api.routesServer.getElection.replace(
new RegExp(':slug', 'g'),
pid
);
const endpoint = new URL(path, api.urlServer);
try {
@ -250,20 +240,19 @@ export const getElection = async (pid: string): Promise<ElectionPayload | HTTPPa
if (response.status != 200) {
const payload = await response.json();
return {status: response.status, msg: payload};
return { status: response.status, msg: payload };
}
const payload = await response.json()
return {...payload, status: response.status};
const payload = await response.json();
return { ...payload, status: response.status };
} catch (error) {
return {status: 400, msg: "Unknown API error"}
return { status: 400, msg: 'Unknown API error' };
}
};
export const castBallot = (
votes: Array<Vote>,
election: ElectionPayload,
token?: string,
token?: string
) => {
/**
* Save a ballot on the remote database
@ -273,31 +262,30 @@ export const castBallot = (
const payload = {
election_ref: election.ref,
votes: votes.map(v => ({
"candidate_id": election.candidates[v.candidateId].id,
"grade_id": election.grades[v.gradeId].id
}))
votes: votes.map((v) => ({
candidate_id: election.candidates[v.candidateId].id,
grade_id: election.grades[v.gradeId].id,
})),
};
if (!election.restricted) {
return fetch(endpoint.href, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
})
}
else {
});
} else {
if (!token) {
throw Error("Missing token")
throw Error('Missing token');
}
return fetch(endpoint.href, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
"Authorization": `Bearer ${token}`
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(payload),
})
});
}
};
@ -331,4 +319,3 @@ export const apiErrors = (error: string): string => {
return 'error.catch22';
}
};

Loading…
Cancel
Save