fix: Davids feedback

pull/100/head
Pierre-Louis Guhur 1 year ago
parent 36681e2392
commit 34620dbebc

@ -182,7 +182,6 @@ const CandidateModal = ({isOpen, position, toggle}) => {
placeholder={t('admin.candidate-desc-placeholder')}
onChange={handleDescription}
value={state.description}
maxLength={250}
/>
</div>
<div className="mt-3 gap-2 d-flex mb-3 justify-content-between">

@ -1,13 +1,13 @@
import { useState, useEffect } from 'react';
import { Col, Label, Input, Modal, ModalBody, Form } from 'reactstrap';
import { faPlus, faArrowLeft } from '@fortawesome/free-solid-svg-icons';
import { useTranslation } from 'next-i18next';
import { ElectionTypes, useElection } from '@services/ElectionContext';
import {useState, useEffect} from 'react';
import {Col, Label, Input, Modal, ModalBody, Form} from 'reactstrap';
import {faPlus, faArrowLeft} from '@fortawesome/free-solid-svg-icons';
import {useTranslation} from 'next-i18next';
import {ElectionTypes, useElection} from '@services/ElectionContext';
import Button from '@components/Button';
import { GradeItem } from '@services/type';
import {GradeItem} from '@services/type';
const GradeModal = ({ isOpen, toggle }) => {
const { t } = useTranslation();
const GradeModal = ({isOpen, toggle}) => {
const {t} = useTranslation();
const [election, dispatch] = useElection();
const [grade, setGrade] = useState<GradeItem>({
@ -19,20 +19,20 @@ const GradeModal = ({ isOpen, toggle }) => {
useEffect(() => {
const maxValue = Math.max(...election.grades.map((g) => g.value));
setGrade({ ...grade, value: maxValue + 1 });
setGrade({...grade, value: maxValue + 1});
}, [election]);
const save = () => {
dispatch({
type: ElectionTypes.SET,
field: 'grades',
value: [...election.grades, grade],
value: [grade, ...election.grades],
});
toggle();
};
const handleName = (e) => {
setGrade((s) => ({ ...s, name: e.target.value }));
setGrade((s) => ({...s, name: e.target.value}));
};
const names = election.grades.map((g) => g.name);

@ -53,12 +53,12 @@ const Grades = () => {
const toggle = () => setVisible((v) => !v);
useEffect(() => {
const defaultGrades = DEFAULT_GRADES.map((g, i) => ({
name: t(g),
value: DEFAULT_GRADES.length - 1 - i,
active: true,
}));
if (election.grades.length < 2) {
const defaultGrades = DEFAULT_GRADES.map((g, i) => ({
name: t(g),
value: DEFAULT_GRADES.length - 1 - i,
active: true,
}));
dispatch({
type: ElectionTypes.SET,
field: 'grades',

@ -6,6 +6,9 @@ import {serverSideTranslations} from 'next-i18next/serverSideTranslations';
import {Container, Row, Col} from 'reactstrap';
import {
faArrowRight,
faCheck,
faCheckToSlot,
faFloppyDisk,
faSquarePollVertical,
} from '@fortawesome/free-solid-svg-icons';
import {getElection, updateElection} from '@services/api';
@ -91,87 +94,83 @@ const Spinner = () => {
);
};
const HeaderRubbon = ({token}) => {
const ManageButtonsMobile = ({handleClosing, waiting}) => {
const {t} = useTranslation();
const [election, dispatch] = useElection();
const [_, dispatchApp] = useAppContext();
const [election, _] = useElection();
const router = useRouter();
const [waiting, setWaiting] = useState(false);
const handleClosing = async () => {
setWaiting(true);
dispatch({
type: ElectionTypes.SET,
field: 'forceClose',
value: true,
});
return (
<>
{!election.restricted && !isClosed(election) && (
<Link href={getUrl(RouteTypes.VOTE, router, election.ref)} className="d-grid">
<Button
icon={faArrowRight}
color="primary"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
position="right"
>
{t('admin.go-to-vote')}
</Button>
</Link>
)}
const candidates = election.candidates
.filter((c) => c.active)
.map((c: CandidateItem) => ({
name: c.name,
description: c.description,
image: c.image,
id: c.id,
}));
const grades = election.grades
.filter((c) => c.active)
.map((g: GradeItem, i: number) => ({name: g.name, value: g.value, id: g.id}));
{canViewResults(election) && (
<Link href={getUrl(RouteTypes.RESULTS, router, election.ref)} className="d-grid">
<Button
icon={faSquarePollVertical}
color="primary"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
position="right"
>
{t('admin.go-to-result')}
</Button>
</Link>
)}
const response = await updateElection(
election.ref,
election.name,
candidates,
grades,
election.description,
election.emails.length,
election.hideResults,
true,
election.restricted,
election.randomOrder,
token
);
if (response.status === 200 && 'ref' in response) {
if (election.restricted && election.emails.length > 0) {
if (election.emails.length !== response.invites.length) {
throw new Error('Unexpected number of invites!');
}
const urlVotes = response.invites.map((token: string) =>
getUrl(RouteTypes.VOTE, router, response.ref, token)
);
const urlResult = getUrl(RouteTypes.RESULTS, router, 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'),
});
}
};
{!isClosed(election) && (
<Button
className="d-grid btn_closing"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
onClick={handleClosing}
position="right"
>
{waiting ? <Spinner /> : t('admin.close-election')}
</Button>
)}
</>
);
};
const HeaderRubbonDesktop = ({handleClosing, handleSubmit, waiting}) => {
const {t} = useTranslation();
const [election, _] = useElection();
const router = useRouter();
return (
<div className="w-100 p-4 bg-primary text-white d-flex justify-content-between align-items-center">
<h5 className="mx-0">{t('admin.admin-title')}</h5>
<div className="d-flex">
<Link href={getUrl(RouteTypes.VOTE, router, election.ref)}>
<Button
icon={faFloppyDisk}
color="primary"
className="me-3"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
position="right"
>
{t('common.save')}
</Button>
</Link>
{!election.restricted && !isClosed(election) && (
<Link href={getUrl(RouteTypes.VOTE, router, election.ref)}>
<Button
icon={faArrowRight}
icon={faCheckToSlot}
color="primary"
className="me-3"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
position="right"
>
{t('admin.go-to-vote')}
{t('common.vote')}
</Button>
</Link>
)}
@ -185,7 +184,7 @@ const HeaderRubbon = ({token}) => {
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
position="right"
>
{t('admin.go-to-result')}
{t('common.results')}
</Button>
</Link>
)}
@ -205,6 +204,17 @@ const HeaderRubbon = ({token}) => {
);
};
const HeaderRubbonMobile = () => {
const {t} = useTranslation();
return (
<div className="w-100 px-4 text-white d-flex justify-content-between align-items-center"
style={{minHeight: "100px"}}>
<h5 className="mx-0 text-white">{t('admin.admin-title')}</h5>
</div>
)
}
const CreateElection = ({context, token}) => {
const {t} = useTranslation();
const [election, dispatch] = useElection();
@ -313,6 +323,68 @@ const CreateElection = ({context, token}) => {
}
};
/**
* Close an election
*/
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,
id: c.id,
}));
const grades = election.grades
.filter((c) => c.active)
.map((g: GradeItem, i: number) => ({name: g.name, value: g.value, id: g.id}));
const response = await updateElection(
election.ref,
election.name,
candidates,
grades,
election.description,
election.emails.length,
election.hideResults,
true,
election.restricted,
election.randomOrder,
token
);
if (response.status === 200 && 'ref' in response) {
if (election.restricted && election.emails.length > 0) {
if (election.emails.length !== response.invites.length) {
throw new Error('Unexpected number of invites!');
}
const urlVotes = response.invites.map((token: string) =>
getUrl(RouteTypes.VOTE, router, response.ref, token)
);
const urlResult = getUrl(RouteTypes.RESULTS, router, 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'),
});
}
};
const numCandidates = election.candidates.filter(
(c) => c.active && c.name != ''
).length;
@ -328,7 +400,12 @@ const CreateElection = ({context, token}) => {
return (
<>
<HeaderRubbon token={token} />
<div className="d-none d-md-flex">
<HeaderRubbonDesktop handleSubmit={handleSubmit} handleClosing={handleClosing} waiting={waiting} />
</div>
<div className="d-flex d-md-none">
<HeaderRubbonMobile />
</div>
<Container
fluid="xl"
className="my-5 flex-column d-flex justify-content-center"
@ -369,6 +446,9 @@ const CreateElection = ({context, token}) => {
>
{waiting ? <Spinner /> : t('admin.confirm-edit')}
</Button>
<div className="d-grid gap-3 mt-3 d-md-none">
<ManageButtonsMobile handleClosing={handleClosing} waiting={waiting} />
</div>
</Container>
</Container>
</>

@ -48,6 +48,7 @@
"common.days": "days",
"common.send": "Send",
"common.vote": "Vote",
"common.results": "Results",
"common.the-vote": "The vote",
"common.the-params": "The parameters",
"common.welcome": "Welcome!",

@ -51,6 +51,7 @@
"common.the-params": "Les paramètres",
"common.vote": "Voter",
"common.welcome": "Bienvenue !",
"common.results": "Résultats",
"error.help": "Besoin d'aide ?",
"error.cant-set-ongoing": "Vous ne pouvez pas modifier ce paramètre pour une élection déjà démarrée.",
"error.at-least-2-candidates": "Ajoutez au moins deux candidats.",

Loading…
Cancel
Save