feat: ballot

pull/89/head
Pierre-Louis Guhur 1 year ago
parent f29d287294
commit 9454f90b34

@ -23,6 +23,7 @@ const CandidateModal = ({isOpen, position, toggle}) => {
const removeCandidate = () => {
dispatch({type: 'candidate-rm', position: position});
toggle();
};
return (

@ -38,13 +38,13 @@ const BallotDesktop = () => {
};
return (
<div className="w-100 h-100 display-none display-lg-block">
<div className="w-100 h-100 d-none d-lg-block">
<TitleBar election={ballot.election} />
<div className="w-100 h-100 d-flex flex-column justify-content-center align-items-center">
<Container className="w-100 h-100 d-flex flex-column justify-content-center align-items-center" style={{maxWidth: "750px"}}>
<h1 className="mb-5">{ballot.election.name}</h1>
{ballot.election.candidates.map((candidate, candidateId) => {
return (
<div key={candidateId} className="bg-white justify-content-between d-flex my-4 py-2 px-3">
<div key={candidateId} className="bg-white justify-content-between d-flex my-4 py-2 w-100 px-3">
<CandidateCard candidate={candidate} />
<div className="d-flex">
{ballot.election.grades.map((_, gradeId) => {
@ -70,7 +70,7 @@ const BallotDesktop = () => {
{t('vote.submit')}
</Button>
</Container>
</div>
</Container>
</div>
)
}

@ -1,20 +1,32 @@
import {MouseEvent} from 'react'
import {MouseEvent, useState} from 'react'
import {useRouter} from 'next/router';
import {useTranslation} from 'next-i18next';
import Button from '@components/Button';
import {Col, Row, Container} from 'reactstrap';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCalendarDays, faCheck} from '@fortawesome/free-solid-svg-icons';
import {faCheck, faChevronLeft, faChevronRight} from '@fortawesome/free-solid-svg-icons';
import {useBallot, BallotTypes, BallotProvider} from '@services/BallotContext';
import CandidateCard from '@components/ballot/CandidateCard'
import TitleBar from '@components/ballot/TitleBar'
import GradeInput from '@components/ballot/GradeInput'
interface TitleInterface {
name: string;
}
const TitleName = ({name}: TitleInterface) => {
return (
<div className="fw-bold text-white w-75 p-4">
{name}
</div>
)
}
const BallotMobile = () => {
const {t} = useTranslation();
const [ballot, dispatch] = useBallot();
const [ballot, _] = useBallot();
const [offset, setOffset] = useState(0);
const numGrades = ballot.election.grades.length;
const disabled = ballot.votes.length !== ballot.election.candidates.length;
@ -25,40 +37,61 @@ const BallotMobile = () => {
router.push(`/confirm/${ballot.election.id}`);
};
const moveRight = (right: boolean) => {
if (right) setOffset(o => o - 247);
else setOffset(o => o + 247);
}
return (
<div className="w-100 h-100 display-block display-lg-none">
<div className="w-100 h-100 d-flex flex-column justify-content-center align-items-center">
<h1 className="mb-5">{ballot.election.name}</h1>
<div className="w-100 h-100 d-block d-lg-none">
<TitleName name={ballot.election.name} />
<div className="w-100 d-flex">
{ballot.election.candidates.map((candidate, candidateId) => {
return (
<div key={candidateId} className="bg-white justify-content-between d-flex my-4 py-2 px-3">
<CandidateCard candidate={candidate} />
<div className="d-flex">
<div key={candidateId} className="bg-white flex-column d-flex my-4 mx-2 py-4 px-3 candidate-vote" style={{"left": offset === 0 ? 0 : offset + 30}}>
<div className="d-flex align-items-center mb-1">
{candidateId !== 0 ?
<div className="me-2"
onClick={() => moveRight(false)}
>
<FontAwesomeIcon color="#0A004C" icon={faChevronLeft} />
</div> : null}
<CandidateCard candidate={candidate} />
{candidateId !== ballot.election.candidates.length - 1 ?
<div className="ms-2" onClick={() => moveRight(true)}><FontAwesomeIcon color="#0A004C" icon={faChevronRight} /></div> : null}
</div>
<div className="d-flex mt-2 flex-column">
{ballot.election.grades.map((_, gradeId) => {
console.assert(gradeId < numGrades);
return (
<GradeInput key={gradeId} gradeId={gradeId} candidateId={candidateId} />
<GradeInput
key={gradeId}
gradeId={gradeId}
candidateId={candidateId}
/>
);
})}
</div>
</div>
);
})}
<Container className="my-5 d-md-flex d-grid justify-content-md-center">
<Button
outline={true}
color="secondary"
className="bg-blue"
onClick={handleSubmit}
disabled={disabled}
icon={faCheck}
position="left"
>
{t('vote.submit')}
</Button>
</Container>
</div>
</div>
<Container className="my-5 d-md-flex d-grid justify-content-md-center">
<Button
outline={true}
color="secondary"
className="bg-blue"
onClick={handleSubmit}
disabled={disabled}
icon={faCheck}
position="left"
>
{t('vote.submit')}
</Button>
</Container>
</div >
)
}

@ -8,7 +8,7 @@ interface CandidateCardInterface {
}
const CandidateCard = ({candidate}: CandidateCardInterface) => {
const {t} = useTranslation();
return (<div className="d-flex align-items-center">
return (<div className="d-flex align-items-center flex-fill">
<Image
src={defaultAvatar}
width={32}

@ -1,7 +1,21 @@
import {useState, useCallback, useEffect, MouseEvent} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faCheck} from '@fortawesome/free-solid-svg-icons';
import {useBallot, BallotTypes} from '@services/BallotContext';
import {getGradeColor} from '@services/grades';
const GradeName = ({name, active}) => {
return (<>
{active ?
<div className="me-3">
<FontAwesomeIcon icon={faCheck} />
</div> : null}
<div className="">
{name}
</div>
</>)
}
interface GradeInputInterface {
gradeId: number;
candidateId: number;
@ -20,14 +34,26 @@ const GradeInput = ({gradeId, candidateId}: GradeInputInterface) => {
const active = ballot.votes.some(b => b.gradeId === gradeId && b.candidateId === candidateId)
const color = active ? getGradeColor(gradeId, numGrades) : '#C3BFD8';
return (
return (<>
<div
className={`text-lg-center my-1 rounded-1 px-2 py-1 fs-5 text-white ms-3`}
className={`justify-content-center d-none d-lg-flex my-1 rounded-1 px-2 py-1 fs-5 text-white ms-3`}
onClick={handleClick}
style={{backgroundColor: color, boxShadow: active ? `0px 2px 0px ${color}` : "0px 2px 0px #8F88BA"}}
>
{grade.name}
<GradeName name={grade.name} active={active} />
</div >
<div
className={`d-flex d-lg-none my-1 justify-content-center py-2 text-white`}
onClick={handleClick}
style={{
backgroundColor: color,
boxShadow: active ? `0px 2px 0px ${color}` : "0px 2px 0px #8F88BA",
width: "200px"
}}
>
<GradeName name={grade.name} active={active} />
</div >
</>
)
}

@ -52,7 +52,7 @@ const GoToBallotConfirm = ({electionId, token}) => {
</Row>
<Row>
<Link href={{pathname: BALLOT, query: {electionId, token}}}>
<Link href={`${BALLOT}/${electionId}/${token ? token : ""}`}>
<Button
color="secondary"
outline={true}
@ -64,7 +64,7 @@ const GoToBallotConfirm = ({electionId, token}) => {
</Link>
</Row>
<Row className="noAds my-0">
<p>{t('resource.noAds')}</p>
<p>{t('home.noAds')}</p>
</Row>
<Row>
<Link href="https://mieuxvoter.fr/le-jugement-majoritaire">

@ -105,14 +105,22 @@ function electionReducer(election: ElectionContextInterface, action) {
const candidate =
action.value === 'default' ? {...defaultCandidate} : action.value;
const candidates = [...election.candidates, candidate];
return {...election, candidates};
console.log("NONACTIVE", candidates.filter(c => !c.active).length)
if (candidates.filter(c => !c.active).length === 0) {
return {
...election, candidates: [...candidates, {...defaultCandidate}]
};
}
else {
return {...election, candidates};
}
}
case 'candidate-rm': {
if (typeof action.position !== 'number') {
throw Error(`Unexpected candidate position ${action.position}`);
}
const candidates = [...election.candidates];
candidates.splice(action.position);
candidates.splice(action.position, 1);
return {...election, candidates};
}
case 'candidate-set': {
@ -126,6 +134,11 @@ function electionReducer(election: ElectionContextInterface, action) {
const candidate = candidates[action.position];
candidate[action.field] = action.value;
candidate['active'] = true;
if (candidates.filter(c => !c.active).length === 0) {
return {
...election, candidates: [...candidates, {...defaultCandidate}]
};
}
return {...election, candidates};
}
case 'grade-push': {

@ -246,3 +246,11 @@
width: auto;
}
}
// EDIT
.candidate-vote {
width: 232px;
position: relative;
transition: left 1s;
}

Loading…
Cancel
Save