fix: set limit date

master
Pierre-Louis Guhur 2 weeks ago
parent e968bfcfcb
commit 125f6e6645

@ -1,36 +1,38 @@
import {useEffect, useState} from 'react';
import {useTranslation} from 'next-i18next';
import {Container, Row, Col} from 'reactstrap';
import { useEffect, useState } from 'react';
import { useTranslation } from 'next-i18next';
import { Container, Row, Col } from 'reactstrap';
import DatePicker from '@components/DatePicker';
import Switch from '@components/Switch';
import {ElectionTypes, useElection} from '@services/ElectionContext';
import { ElectionTypes, useElection } from '@services/ElectionContext';
const LimitDate = () => {
const {t} = useTranslation();
const { t } = useTranslation();
const [election, dispatch] = useElection();
const defaultEndDate = new Date();
defaultEndDate.setUTCDate(defaultEndDate.getUTCDate() + 15);
const [endDate, setEndDate] = useState(election.dateEnd ? new Date(election.dateEnd) : defaultEndDate);
const [endDate, setEndDate] = useState(
election.dateEnd ? new Date(election.dateEnd) : defaultEndDate
);
const hasDate = election.dateEnd !== null;
const toggle = () => {
dispatch({
type: ElectionTypes.SET,
field: 'dateEnd',
value: hasDate ? null : endDate,
value: hasDate ? null : endDate.toISOString(),
});
};
const handleDate = (date) => {
setEndDate(date)
setEndDate(date);
dispatch({
type: ElectionTypes.SET,
field: 'dateEnd',
value: date.toString(),
value: date.toISOString(),
});
}
};
const desc = t('admin.limit-duration-desc');
const now = new Date();
@ -44,7 +46,6 @@ const LimitDate = () => {
}
}, [election.dateEnd]);
return (
<Container className="bg-white p-3 p-md-4 mt-1">
<div className="d-flex">

@ -1,9 +1,9 @@
import {useEffect, useState} from 'react';
import {useRouter} from 'next/router';
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 { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { Container, Row, Col } from 'reactstrap';
import {
faArrowRight,
faCheck,
@ -12,7 +12,13 @@ import {
faSquarePollVertical,
faSquareXmark,
} from '@fortawesome/free-solid-svg-icons';
import {getElection, updateElection, closeElection, getProgress, openElection} from '@services/api';
import {
getElection,
updateElection,
closeElection,
getProgress,
openElection,
} from '@services/api';
import {
ElectionContextInterface,
ElectionProvider,
@ -25,8 +31,8 @@ import {
hasEnoughCandidates,
canBeFinished,
} from '@services/ElectionContext';
import {CandidateItem, GradeItem} from '@services/type';
import {gradeColors} from '@services/grades';
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';
@ -37,13 +43,13 @@ import Order from '@components/admin/Order';
import Private from '@components/admin/Private';
import ErrorMessage from '@components/Error';
import Blur from '@components/Blur';
import {getUrl, RouteTypes} from '@services/routes';
import {sendInviteMails} from '@services/mail';
import {AppTypes, useAppContext} from '@services/context';
import {getLocaleShort} from '@services/utils';
import { getUrl, RouteTypes } from '@services/routes';
import { sendInviteMails } from '@services/mail';
import { AppTypes, useAppContext } from '@services/context';
import { getLocaleShort } from '@services/utils';
export async function getServerSideProps({query, locale}) {
const {pid, tid: token} = query;
export async function getServerSideProps({ query, locale }) {
const { pid, tid: token } = query;
const electionRef = pid.replaceAll('-', '');
const [payload, translations, progress] = await Promise.all([
@ -53,15 +59,14 @@ export async function getServerSideProps({query, locale}) {
]);
if ('message' in payload) {
return {props: {err: payload.message, ...translations}};
return { props: { err: payload.message, ...translations } };
}
if ('message' in progress) {
return {props: {err: progress.message, ...translations}};
return { props: { err: progress.message, ...translations } };
}
const grades = payload.grades.map((g) => ({...g, active: true}));
const grades = payload.grades.map((g) => ({ ...g, active: true }));
const candidates: Array<CandidateItem> = payload.candidates.map((c) => ({
...c,
@ -104,8 +109,8 @@ const Spinner = () => {
);
};
const ManageButtonsMobile = ({handleClosing, waiting}) => {
const {t} = useTranslation();
const ManageButtonsMobile = ({ handleClosing, waiting }) => {
const { t } = useTranslation();
const [election, _] = useElection();
const router = useRouter();
const locale = getLocaleShort(router);
@ -113,11 +118,14 @@ const ManageButtonsMobile = ({handleClosing, waiting}) => {
return (
<>
{!election.restricted && !isClosed(election) && (
<Link href={getUrl(RouteTypes.VOTE, locale, election.ref)} className="d-grid">
<Link
href={getUrl(RouteTypes.VOTE, locale, election.ref)}
className="d-grid"
>
<Button
icon={faArrowRight}
color="primary"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
position="right"
>
{t('admin.go-to-vote')}
@ -126,11 +134,14 @@ const ManageButtonsMobile = ({handleClosing, waiting}) => {
)}
{canViewResults(election) && (
<Link href={getUrl(RouteTypes.RESULTS, locale, election.ref)} className="d-grid">
<Link
href={getUrl(RouteTypes.RESULTS, locale, election.ref)}
className="d-grid"
>
<Button
icon={faSquarePollVertical}
color="primary"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
position="right"
>
{t('admin.go-to-result')}
@ -141,7 +152,7 @@ const ManageButtonsMobile = ({handleClosing, waiting}) => {
{!isClosed(election) && (
<Button
className="d-grid 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"
>
@ -151,8 +162,13 @@ const ManageButtonsMobile = ({handleClosing, waiting}) => {
</>
);
};
const HeaderRubbonDesktop = ({handleClosing, handleOpening, handleSubmit, waiting}) => {
const {t} = useTranslation();
const HeaderRubbonDesktop = ({
handleClosing,
handleOpening,
handleSubmit,
waiting,
}) => {
const { t } = useTranslation();
const [election, _] = useElection();
const router = useRouter();
const locale = getLocaleShort(router);
@ -167,7 +183,7 @@ const HeaderRubbonDesktop = ({handleClosing, handleOpening, handleSubmit, waitin
color="primary"
className="me-3"
onClick={handleSubmit}
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
position="right"
>
{t('common.save')}
@ -178,7 +194,7 @@ const HeaderRubbonDesktop = ({handleClosing, handleOpening, handleSubmit, waitin
icon={faCheckToSlot}
color="primary"
className="me-3"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
position="right"
>
{t('common.vote')}
@ -192,7 +208,7 @@ const HeaderRubbonDesktop = ({handleClosing, handleOpening, handleSubmit, waitin
icon={faSquarePollVertical}
color="primary"
className="me-3"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
position="right"
>
{t('common.results')}
@ -204,44 +220,44 @@ const HeaderRubbonDesktop = ({handleClosing, handleOpening, handleSubmit, waitin
<Button
className="me-3"
color="primary"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
onClick={handleOpening}
icon={faSquareXmark}
position="right"
>
{waiting ? <Spinner /> : t('admin.open-election')}
</Button>
)
: (
<Button
className="me-3"
color="primary"
style={{border: '2px solid rgba(255, 255, 255, 0.4)'}}
onClick={handleClosing}
icon={faCheck}
position="right"
>
{waiting ? <Spinner /> : t('admin.close-election')}
</Button>
)}
) : (
<Button
className="me-3"
color="primary"
style={{ border: '2px solid rgba(255, 255, 255, 0.4)' }}
onClick={handleClosing}
icon={faCheck}
position="right"
>
{waiting ? <Spinner /> : t('admin.close-election')}
</Button>
)}
</div>
</div >
</div>
);
};
const HeaderRubbonMobile = () => {
const {t} = useTranslation();
const { t } = useTranslation();
return (
<div className="w-100 px-4 text-white d-flex justify-content-between align-items-center"
style={{minHeight: "100px"}}>
<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 ManageElection = ({token}) => {
const {t} = useTranslation();
const ManageElection = ({ token }) => {
const { t } = useTranslation();
const [election, dispatch] = useElection();
const [_, dispatchApp] = useAppContext();
const router = useRouter();
@ -294,7 +310,7 @@ const ManageElection = ({token}) => {
}));
const grades = election.grades
.filter((c) => c.active)
.map((g: GradeItem) => ({name: g.name, value: g.value, id: g.id}));
.map((g: GradeItem) => ({ name: g.name, value: g.value, id: g.id }));
setWaiting(true);
const response = await updateElection(
@ -340,7 +356,6 @@ const ManageElection = ({token}) => {
field: 'emails',
value: [],
});
}
setWaiting(false);
@ -353,7 +368,7 @@ const ManageElection = ({token}) => {
}
};
/**
/**
* Open an election
*/
const handleOpening = async () => {
@ -371,11 +386,10 @@ const ManageElection = ({token}) => {
field: 'forceClose',
value: false,
});
}
};
/**
/**
* Close an election
*/
const handleClosing = async () => {
@ -402,7 +416,7 @@ const ManageElection = ({token}) => {
const numGrades = election.grades.filter(
(g) => g.active && g.name != ''
).length;
console.log(election.name, numCandidates, numGrades)
console.log(election.name, numCandidates, numGrades);
const disabled =
!election.name ||
election.name == '' ||
@ -413,10 +427,12 @@ const ManageElection = ({token}) => {
return (
<>
<div className="d-none d-md-flex">
<HeaderRubbonDesktop handleSubmit={handleSubmit}
<HeaderRubbonDesktop
handleSubmit={handleSubmit}
handleClosing={handleClosing}
handleOpening={handleOpening}
waiting={waiting} />
waiting={waiting}
/>
</div>
<div className="d-flex d-md-none">
<HeaderRubbonMobile />
@ -429,12 +445,16 @@ const ManageElection = ({token}) => {
<Col className={isClosed(election) ? 'col-12' : 'col-lg-3 col-12'}>
<Container className="py-4 d-none d-md-block">
<h4>{t('common.the-vote')}</h4>
{election.restricted && election.numVoters !== undefined &&
<h5>{t('admin.num-voters')} {election.numVoters}</h5>
}
{election.numVoted !== undefined &&
<h5>{t('admin.num-voted')} {election.numVoted}</h5>
}
{election.restricted && election.numVoters !== undefined && (
<h5>
{t('admin.num-voters')} {election.numVoters}
</h5>
)}
{election.numVoted !== undefined && (
<h5>
{t('admin.num-voted')} {election.numVoted}
</h5>
)}
</Container>
<TitleField defaultName={election.name} />
<CandidatesConfirmField />
@ -465,7 +485,10 @@ const ManageElection = ({token}) => {
{waiting ? <Spinner /> : t('admin.confirm-edit')}
</Button>
<div className="d-grid gap-3 mt-3 d-md-none">
<ManageButtonsMobile handleClosing={handleClosing} waiting={waiting} />
<ManageButtonsMobile
handleClosing={handleClosing}
waiting={waiting}
/>
</div>
</Container>
</Container>
@ -473,8 +496,8 @@ const ManageElection = ({token}) => {
);
};
const ManageElectionProviding = ({err, context, token}) => {
const {t} = useTranslation();
const ManageElectionProviding = ({ err, context, token }) => {
const { t } = useTranslation();
if (err) {
return <ErrorMessage>{t('admin.error')}</ErrorMessage>;
}

@ -1,5 +1,5 @@
import {Candidate, Grade, Vote} from './type';
import {URL_SERVER} from './constants';
import { Candidate, Grade, Vote } from './type';
import { URL_SERVER } from './constants';
export const api = {
routesServer: {
@ -73,8 +73,8 @@ export interface ElectionUpdatedPayload extends ElectionPayload {
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 {
@ -196,13 +196,13 @@ export const updateElection = async (
});
if (!req.ok || req.status !== 200) {
const payload = await req.json();
return {status: req.status, ...payload};
return { status: req.status, ...payload };
}
const payload = await req.json();
return {status: 200, ...payload};
return { status: 200, ...payload };
} catch (e) {
console.error(e);
return {status: 400, message: `Unknown API error: ${e}`};
return { status: 400, message: `Unknown API error: ${e}` };
}
};
@ -226,13 +226,13 @@ export const closeElection = async (
});
if (!req.ok || req.status !== 200) {
const payload = await req.json();
return {status: req.status, ...payload};
return { status: req.status, ...payload };
}
const payload = await req.json();
return {status: 200, ...payload};
return { status: 200, ...payload };
} catch (e) {
console.error(e);
return {status: 400, message: `Unknown API error: ${e}`};
return { status: 400, message: `Unknown API error: ${e}` };
}
};
@ -256,13 +256,13 @@ export const openElection = async (
});
if (!req.ok || req.status !== 200) {
const payload = await req.json();
return {status: req.status, ...payload};
return { status: req.status, ...payload };
}
const payload = await req.json();
return {status: 200, ...payload};
return { status: 200, ...payload };
} catch (e) {
console.error(e);
return {status: 400, message: `Unknown API error: ${e}`};
return { status: 400, message: `Unknown API error: ${e}` };
}
};
@ -272,7 +272,6 @@ export const openElection = async (
export const getResults = async (
pid: string
): Promise<ResultsPayload | HTTPPayload> => {
const endpoint = new URL(
api.routesServer.getResults.replace(new RegExp(':slug', 'g'), pid),
URL_SERVER
@ -282,13 +281,13 @@ export const getResults = async (
const response = await fetch(endpoint.href);
if (response.status != 200) {
const payload = await response.json();
return {status: response.status, ...payload};
return { status: response.status, ...payload };
}
const payload = await response.json();
return {...payload, status: response.status};
return { ...payload, status: response.status };
} catch (error) {
console.error(error);
return {status: 400, message: `Unknown API error: ${error}`};
return { status: 400, message: `Unknown API error: ${error}` };
}
};
@ -309,19 +308,18 @@ export const getElection = async (
if (response.status != 200) {
const payload = await response.json();
return {status: response.status, message: payload};
return { status: response.status, message: payload };
}
const payload = await response.json();
return {...payload, status: response.status};
return { ...payload, status: response.status };
} catch (error) {
return {status: 400, message: 'Unknown API error'};
return { status: 400, message: 'Unknown API error' };
}
};
export const getProgress = async (
pid: string,
token: string,
token: string
): Promise<ProgressPayload | HTTPPayload> => {
/**
* Fetch progress (number of voters) from external API
@ -333,24 +331,21 @@ export const getProgress = async (
const endpoint = new URL(path, URL_SERVER);
try {
const response = await fetch(
endpoint.href, {
method: "GET",
const response = await fetch(endpoint.href, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
}
})
;
},
});
if (response.status != 200) {
const payload = await response.json();
return {status: response.status, message: payload};
return { status: response.status, message: payload };
}
const payload = await response.json();
return {...payload, status: response.status};
return { ...payload, status: response.status };
} catch (error) {
return {status: 400, message: 'Unknown API error'};
return { status: 400, message: 'Unknown API error' };
}
};
@ -360,7 +355,7 @@ export const getProgress = async (
export const getBallot = async (
token: string
): Promise<ElectionPayload | HTTPPayload> => {
const path = api.routesServer.voteElection
const path = api.routesServer.voteElection;
const endpoint = new URL(path, URL_SERVER);
if (!token) {
@ -376,14 +371,13 @@ export const getBallot = async (
});
if (response.status != 200) {
return {status: response.status, message: "Can not load this ballot"};
return { status: response.status, message: 'Can not load this ballot' };
}
const payload = await response.json();
return {...payload, status: response.status};
return { ...payload, status: response.status };
} catch (error) {
return {status: 400, message: 'Unknown API error'};
return { status: 400, message: 'Unknown API error' };
}
};
@ -409,7 +403,7 @@ export const castBallot = (
if (!election.restricted) {
return fetch(endpoint.href, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
} else {

Loading…
Cancel
Save