diff --git a/components/Error.jsx b/components/Error.jsx index 5ece508..dc855db 100644 --- a/components/Error.jsx +++ b/components/Error.jsx @@ -1,35 +1,40 @@ -import Link from 'next/link' -import {Container, Row, Col} from "reactstrap"; -import {useTranslation} from "next-i18next"; +import Link from "next/link"; +import { Container, Row, Col } from "reactstrap"; +import { useTranslation } from "next-i18next"; - -const Error = props => { - const {t} = useTranslation(); +const Error = (props) => { + const { t } = useTranslation(); return ( - - - - - logo - - - - - -

{props.value}

- -
- - + + - - { t("common.backHomepage") } - + + logo + - - - -); -} + + + +

{props.value}

+ +
+ + + + {t("common.backHomepage")} + + + + + {t("resource.help")} + + + +
+ ); +}; -export default Error +export default Error; diff --git a/components/layouts/Footer.jsx b/components/layouts/Footer.jsx index 3163ac4..ab22ec0 100644 --- a/components/layouts/Footer.jsx +++ b/components/layouts/Footer.jsx @@ -40,7 +40,7 @@ const Footer = () => { className={bboxLink3.top === bboxLink4.top ? "" : "no-tack"} > - {t("Need help?")} + {t("resource.help")}
  • ({ ok: true, ...res }), - (err) => ({ ok: false, err }) - ), + let [details, translations] = await Promise.all([ + getDetails(pid), serverSideTranslations(locale, [], config), ]); - if (!res.ok) { - return { props: { err: res.err, ...translations } }; + if (details.includes(ELECTION_NOT_STARTED_ERROR)) { + details = { title: "", on_invitation_only: true, restrict_results: true }; + } else { + if (typeof details === "string" || details instanceof String) { + return { props: { err: details, ...translations } }; + } + + if (!details.title) { + return { props: { err: "Unknown error", ...translations } }; + } } return { props: { - invitationOnly: res.on_invitation_only, - restrictResults: res.restrict_results, - title: res.title, + invitationOnly: details.on_invitation_only, + restrictResults: details.restrict_results, + title: details.title, pid: pid, ...translations, }, @@ -50,12 +59,12 @@ const ConfirmElection = ({ pid, err, }) => { + const { t } = useTranslation(); + if (err) { - return ; + return ; } - const { t } = useTranslation(); - const origin = typeof window !== "undefined" && window.location.origin ? window.location.origin diff --git a/pages/result/[pid]/[[...tid]].jsx b/pages/result/[pid]/[[...tid]].jsx index 001c5b1..c206c53 100644 --- a/pages/result/[pid]/[[...tid]].jsx +++ b/pages/result/[pid]/[[...tid]].jsx @@ -24,33 +24,28 @@ export async function getServerSideProps({ query, locale }) { const { pid, tid } = query; const [res, details, translations] = await Promise.all([ - getResults( - pid, - (res) => ({ ok: true, res }), - (err) => { - return { ok: false, err: "Unknown error" }; - } - ), - getDetails( - pid, - (res) => ({ ok: true, ...res }), - (err) => ({ ok: false, err: "Unknown error" }) - ), + getResults(pid), + getDetails(pid), serverSideTranslations(locale, [], config), ]); - if (!res.ok) { - return { props: { err: res.err, ...translations } }; + if (typeof res === "string" || res instanceof String) { + return { props: { err: res.slice(1, -1), ...translations } }; } - if (!details.ok) { - return { props: { err: details.err, ...translations } }; + + if (typeof details === "string" || details instanceof String) { + return { props: { err: res.slice(1, -1), ...translations } }; + } + + if (!details.candidates || !Array.isArray(details.candidates)) { + return { props: { err: "Unknown error", ...translations } }; } return { props: { title: details.title, numGrades: details.num_grades, - candidates: res.res, + candidates: res, pid: pid, ...translations, }, diff --git a/pages/vote/[pid]/[[...tid]].jsx b/pages/vote/[pid]/[[...tid]].jsx index 943f472..ffaaa8f 100644 --- a/pages/vote/[pid]/[[...tid]].jsx +++ b/pages/vote/[pid]/[[...tid]].jsx @@ -15,37 +15,29 @@ import config from "../../../next-i18next.config.js"; const shuffle = (array) => array.sort(() => Math.random() - 0.5); export async function getServerSideProps({ query: { pid, tid }, locale }) { - const [res, translations] = await Promise.all([ - getDetails( - pid, - (res) => { - console.log("DETAILS:", res); - return { ok: true, ...res }; - }, - (err) => { - console.log("ERR:", err); - return { ok: false, err: "Unknown error" }; - } - ), + const [details, translations] = await Promise.all([ + getDetails(pid), serverSideTranslations(locale, [], config), ]); - if (!res.ok) { - return { props: { err: res.err, ...translations } }; + if (typeof details === "string" || details instanceof String) { + return { props: { err: details, ...translations } }; } - console.log(res); + if (!details.candidates || !Array.isArray(details.candidates)) { + return { props: { err: "Unknown error", ...translations } }; + } - shuffle(res.candidates); + shuffle(details.candidates); return { props: { ...translations, - invitationOnly: res.on_invitation_only, - restrictResults: res.restrict_results, - candidates: res.candidates.map((name, i) => ({ id: i, label: name })), - title: res.title, - numGrades: res.num_grades, + invitationOnly: details.on_invitation_only, + restrictResults: details.restrict_results, + candidates: details.candidates.map((name, i) => ({ id: i, label: name })), + title: details.title, + numGrades: details.num_grades, pid: pid, token: tid || null, }, @@ -53,8 +45,10 @@ export async function getServerSideProps({ query: { pid, tid }, locale }) { } const VoteBallot = ({ candidates, title, numGrades, pid, err, token }) => { + const { t } = useTranslation(); + if (err) { - return ; + return ; } const [judgments, setJudgments] = useState([]); @@ -67,7 +61,6 @@ const VoteBallot = ({ candidates, title, numGrades, pid, err, token }) => { const router = useRouter(); - const { t } = useTranslation(); const allGrades = translateGrades(t); const grades = allGrades.filter( (grade) => grade.value >= allGrades.length - numGrades diff --git a/pages/vote/[pid]/confirm.jsx b/pages/vote/[pid]/confirm.jsx index a02ac0c..7f1ffc2 100644 --- a/pages/vote/[pid]/confirm.jsx +++ b/pages/vote/[pid]/confirm.jsx @@ -5,38 +5,43 @@ import { useTranslation } from "next-i18next"; import { serverSideTranslations } from "next-i18next/serverSideTranslations"; import Paypal from "@components/banner/Paypal"; import Gform from "@components/banner/Gform"; -import { getDetails } from "@services/api"; +import Error from "@components/Error"; +import { getDetails, apiErrors } from "@services/api"; import config from "../../../next-i18next.config.js"; export async function getServerSideProps({ query: { pid }, locale }) { - const [res, translations] = await Promise.all([ - getDetails( - pid, - (res) => ({ ok: true, ...res }), - (err) => ({ ok: false, err }) - ), + const [details, translations] = await Promise.all([ + getDetails(pid), serverSideTranslations(locale, [], config), ]); - if (!res.ok) { - return { props: { err: res.err, ...translations } }; + if (typeof details === "string" || details instanceof String) { + return { props: { err: res.slice(1, -1), ...translations } }; + } + + if (!details.candidates || !Array.isArray(details.candidates)) { + return { props: { err: "Unknown error", ...translations } }; } return { props: { ...translations, - invitationOnly: res.on_invitation_only, - restrictResults: res.restrict_results, - candidates: res.candidates.map((name, i) => ({ id: i, label: name })), - title: res.title, - numGrades: res.num_grades, + invitationOnly: details.on_invitation_only, + restrictResults: details.restrict_results, + candidates: details.candidates.map((name, i) => ({ id: i, label: name })), + title: details.title, + numGrades: details.num_grades, pid: pid, }, }; } -const VoteSuccess = ({ title, invitationOnly, pid }) => { +const VoteSuccess = ({ title, invitationOnly, pid, err }) => { const { t } = useTranslation(); + if (err && err !== "") { + return ; + } + return ( diff --git a/public/locales/de/resource.json b/public/locales/de/resource.json index 7c958ad..0d53e1a 100644 --- a/public/locales/de/resource.json +++ b/public/locales/de/resource.json @@ -6,7 +6,7 @@ "Privacy policy": "Datenschutzerklärung", "resource.legalNotices": "Rechtliche Hinweise", "FAQ": "FAQ", - "Need help?": "Brauchen Sie Hilfe?", + "resource.help": "Brauchen Sie Hilfe?", "BetterVote": " BetterVote", "Voting platform": "Wahlplattform", "Majority Judgment": " Mehrheitswahl ", diff --git a/public/locales/en/error.json b/public/locales/en/error.json new file mode 100644 index 0000000..0ef4f28 --- /dev/null +++ b/public/locales/en/error.json @@ -0,0 +1,11 @@ +{ + "error.e1": "Oops... The election is unknown", + "error.e2": "The election is still going on. You can't access now to the results.", + "error.e3": "No votes have been recorded yet. Come back later.", + "error.e4": "The election has not started yet.", + "error.e5": "The election is over. You can't vote anymore", + "error.e6": "You need a token to vote in this election", + "error.e7": "You seem to have already voted.", + "error.e8": "The parameters of the election are incorrect.", + "error.catch22": "Unknown error" +} diff --git a/public/locales/en/resource.json b/public/locales/en/resource.json index 4502c91..2fef84f 100644 --- a/public/locales/en/resource.json +++ b/public/locales/en/resource.json @@ -6,7 +6,7 @@ "Privacy policy": "Privacy policy", "resource.legalNotices": "Legal notices", "FAQ": "FAQ", - "Need help?": "Need help?", + "resource.help": "Need help?", "BetterVote": "BetterVote", "Voting platform": "Voting platform", "Majority Judgment": "Majority Judgment", diff --git a/public/locales/es/resource.json b/public/locales/es/resource.json index db133b3..610f24d 100644 --- a/public/locales/es/resource.json +++ b/public/locales/es/resource.json @@ -6,7 +6,7 @@ "Privacy policy": "Política de privacidad", "resource.legalNotices": "Avisos legales", "FAQ": "FAQ", - "Need help?": "¿Necesitas ayuda?", + "resource.help": "¿Necesitas ayuda?", "BetterVote": "VotarMejor", "Voting platform": "Plataforma de votación", "Majority Judgment": "Juicio Mayoritario", diff --git a/public/locales/fr/error.json b/public/locales/fr/error.json new file mode 100644 index 0000000..1932725 --- /dev/null +++ b/public/locales/fr/error.json @@ -0,0 +1,11 @@ +{ + "error.e1": "Impossible de retrouver le vote en question", + "error.e2": "L'élection est encore en cours. Revenez plus tard.", + "error.e3": "Aucun vote n'a encore été enregistré. Revenez plus tard.", + "error.e4": "L'élection n'a pas encore démarrée.", + "error.e5": "L'élection est terminée. Vous ne pouvez plus voter.", + "error.e6": "Vous avez besoin d'un jeton pour participer à cette élection.", + "error.e7": "Vous avez déjà voté pour cette élection.", + "error.e8": "Les paramètres de l'élection sont inconnues.", + "error.catch22": "Erreur inconnue." +} diff --git a/public/locales/fr/resource.json b/public/locales/fr/resource.json index 31adea2..b8abf2b 100644 --- a/public/locales/fr/resource.json +++ b/public/locales/fr/resource.json @@ -6,7 +6,7 @@ "Privacy policy": "Politique de confidentialité", "resource.legalNotices": "Mentions légales", "FAQ": "FAQ", - "Need help?": "Besoin d'aide ?", + "resource.help": "Besoin d'aide ?", "BetterVote": "MieuxVoter", "Voting platform": "Plateforme de vote", "Majority Judgment": "Jugement Majoritaire", @@ -15,7 +15,7 @@ "Delete?": "Supprimer ?", "Are you sure to delete": "Êtes-vous sûr(e) de supprimer", "the row": "la ligne", - "resource.writeQuestion": "Décrire ici votre question ou introduire simplement votre vote (250 caractères max.)", + "resource.writeQuestionHere": "Décrire ici votre question ou introduire simplement votre vote (250 caractères max.)", "Enter the name of your candidate or proposal here (250 characters max.)": "Saisissez ici le nom de votre candidat ou de votre proposition (250 caractères max.)", "Please add at least 2 candidates.": "Merci d'ajouter au moins 2 candidats.", "resource.questionLabel": "Question de votre vote", diff --git a/public/locales/ru/resource.json b/public/locales/ru/resource.json index 26de7e8..785664d 100644 --- a/public/locales/ru/resource.json +++ b/public/locales/ru/resource.json @@ -6,7 +6,7 @@ "Privacy policy": "Политика конфиденциальности", "resource.legalNotices": "Официальные уведомления", "FAQ": "Часто задаваемые вопросы", - "Need help?": "Нужна помощь?", + "resource.help": "Нужна помощь?", "BetterVote": "BetterVote", "Voting platform": "Платформа голосования", "Majority Judgment": "Решение Большинства", diff --git a/services/api.js b/services/api.js index 5939a14..bb5489c 100644 --- a/services/api.js +++ b/services/api.js @@ -16,7 +16,7 @@ const sendInviteMail = (res) => { /** * Send an invitation mail using a micro-service with Netlify */ - const { title, mails, tokens, locale } = res; + const { id, title, mails, tokens, locale } = res; if (!mails || !mails.length) { throw new Error("No emails are provided."); @@ -30,14 +30,14 @@ const sendInviteMail = (res) => { typeof window !== "undefined" && window.location.origin ? window.location.origin : "http://localhost"; - const urlVote = (pid) => new URL(`/vote/${pid}`, origin); + const urlVote = (pid, token) => new URL(`/vote/${pid}/${token}`, origin); const urlResult = (pid) => new URL(`/result/${pid}`, origin); const recipientVariables = {}; tokens.forEach((token, index) => { recipientVariables[mails[index]] = { - urlVote: urlVote(token), - urlResult: urlResult(token), + urlVote: urlVote(id, token), + urlResult: urlResult(id), }; }); @@ -146,15 +146,13 @@ const getDetails = (pid, successCallback, failureCallback) => { return fetch(detailsEndpoint.href) .then((response) => { if (!response.ok) { - console.log("NOK", response); return Promise.reject(response.text()); } - const res = response.json(); - console.log("OK", res); - return res; + return response.json(); }) .then(successCallback || ((res) => res)) - .catch(failureCallback || ((err) => err)); + .catch(failureCallback || ((err) => err)) + .then((res) => res); }; const castBallot = (judgments, pid, token, callbackSuccess, callbackError) => { @@ -193,30 +191,30 @@ export const WRONG_ELECTION_ERROR = "E9:"; export const apiErrors = (error, t) => { if (error.includes(UNKNOWN_ELECTION_ERROR)) { - return t("Oops... The election is unknown."); + return t("error.e1"); } if (error.includes(ONGOING_ELECTION_ERROR)) { - return t( - "The election is still going on. You can't access now to the results." - ); + return t("error.e2"); } if (error.includes(NO_VOTE_ERROR)) { - return t("No votes have been recorded yet. Come back later."); + return t("error.e3"); } if (error.includes(ELECTION_NOT_STARTED_ERROR)) { - return t("The election has not started yet."); + return t("error.e4"); } if (error.includes(ELECTION_FINISHED_ERROR)) { - return t("The election is over. You can't vote anymore"); + return t("error.e5"); } if (error.includes(INVITATION_ONLY_ERROR)) { - return t("You need a token to vote in this election"); + return t("error.e6"); } if (error.includes(USED_TOKEN_ERROR)) { - return t("You seem to have already voted."); + return t("error.e7"); } if (error.includes(WRONG_ELECTION_ERROR)) { - return t("The parameters of the election are incorrect."); + return t("error.e8"); + } else { + return t("error.catch22"); } };