wip: refactor

pull/89/head
Pierre-Louis Guhur 1 year ago
parent 56836e5ea1
commit c8ae7147e6

@ -0,0 +1,30 @@
import {UncontrolledAlert} from 'reactstrap';
import {useTranslation} from "next-i18next";
const AlertDismissible = ({msg, color}) => {
const {t} = useTranslation();
if (msg) {
return (
<UncontrolledAlert color={color}>
<h4 className={`${color}-heading`}>{t(msg)}</h4>
<p>
<a
href={`mailto:${CONTACT_MAIL}?subject=[HELP]`}
className="btn btn-success m-auto"
>
{t("error.help")}
</a>
</p>
</UncontrolledAlert >
);
}
return null;
}
AlertDismissible.defaultProps = {
color: 'danger'
};
export default AlertDismissible;

@ -1,9 +1,10 @@
import Link from "next/link";
import { Container, Row, Col } from "reactstrap";
import { useTranslation } from "next-i18next";
import {Container, Row, Col} from "reactstrap";
import {useTranslation} from "next-i18next";
import {CONTACT_MAIL} from '@services/constants';
const Error = (props) => {
const { t } = useTranslation();
const Error = ({msg}) => {
const {t} = useTranslation();
return (
<Container className="full-height-container">
<Row>
@ -15,25 +16,25 @@ const Error = (props) => {
</Row>
<Row className="mt-4">
<Col className="text-center">
<h4>{props.value}</h4>
<h4>{t(msg)}</h4>
</Col>
</Row>
<Row className="mt-4">
<Col className="my-3" sm="6">
<Link href="/">
<a className="btn btn-secondary m-auto">{t("common.backHomepage")}</a>
<a className="btn btn-secondary m-auto">{t("common.back-homepage")}</a>
</Link>
</Col>
<Col className="my-3" sm="6">
<a
href="mailto:app@mieuxvoter.fr?subject=[HELP]"
href={`mailto:${CONTACT_MAIL}?subject=[HELP]`}
className="btn btn-success m-auto"
>
{t("resource.help")}
{t("error.help")}
</a>
</Col>
</Row>
</Container>
</Container >
);
};

@ -16,7 +16,7 @@ const Logo = props => {
return (
<Image
src={src}
alt={t('logo-alt')}
alt={t('logo.alt')}
className="d-block"
{...props}
/>

@ -1,7 +1,6 @@
import React, {useEffect, useRef, useState} from "react";
import ReactDOM from "react-dom";
import styled from "styled-components";
// TODO use bootstrap modal
// https://getbootstrap.com/docs/5.0/components/modal/
//
const Modal = ({show, onClose, children, title}) => {
const handleCloseClick = (e) => {
@ -10,17 +9,17 @@ const Modal = ({show, onClose, children, title}) => {
};
const modalContent = show ? (
<StyledModalOverlay>
<StyledModal>
<StyledModalHeader>
<div className='vh-100 modal overlay'>
<div className='modal body'>
<div className='modal header'>
<a href="#" onClick={handleCloseClick}>
x
</a>
</StyledModalHeader>
{title && <StyledModalTitle>{title}</StyledModalTitle>}
<StyledModalBody>{children}</StyledModalBody>
</StyledModal>
</StyledModalOverlay>
</div>
{title && <div>{title}</div>}
<div className='pt-5'>{children}</div>
</div>
</div>
) : null;
@ -30,34 +29,5 @@ const Modal = ({show, onClose, children, title}) => {
};
const StyledModalBody = styled.div`
padding-top: 10px;
`;
const StyledModalHeader = styled.div`
display: flex;
justify-content: flex-end;
font-size: 25px;
`;
const StyledModal = styled.div`
background: white;
width: 500px;
height: 600px;
border-radius: 15px;
padding: 15px;
`;
const StyledModalTitle = styled.div
const StyledModalOverlay = styled.div`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: rgba(0, 0, 0, 0.5);
`;
export default Modal;

@ -0,0 +1,63 @@
import {createContext, useContext, useReducer} from 'react';
const TasksContext = createContext(null);
const TasksDispatchContext = createContext(null);
export function TasksProvider({children}) {
const [tasks, dispatch] = useReducer(
tasksReducer,
initialTasks
);
return (
<TasksContext.Provider value={tasks}>
<TasksDispatchContext.Provider
value={dispatch}
>
{children}
</TasksDispatchContext.Provider>
</TasksContext.Provider>
);
}
export function useTasks() {
return useContext(TasksContext);
}
export function useTasksDispatch() {
return useContext(TasksDispatchContext);
}
function tasksReducer(tasks, action) {
switch (action.type) {
case 'added': {
return [...tasks, {
id: action.id,
text: action.text,
done: false
}];
}
case 'changed': {
return tasks.map(t => {
if (t.id === action.task.id) {
return action.task;
} else {
return t;
}
});
}
case 'deleted': {
return tasks.filter(t => t.id !== action.id);
}
default: {
throw Error('Unknown action: ' + action.type);
}
}
}
const initialTasks = [
{id: 0, text: 'Philosophers Path', done: true},
{id: 1, text: 'Visit the temple', done: false},
{id: 2, text: 'Drink matcha', done: false}
];

@ -1,24 +0,0 @@
import { useState } from 'react'
import { Alert, Button } from 'react-bootstrap';
import { faTimes, faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
export default function AlertDismissibleExample() {
const [show, setShow] = useState(true);
if (show) {
return (
<Alert className="preventWarning">
<Alert.Heading>
<div>
<FontAwesomeIcon icon={faExclamationCircle} className="mr-2" />
<span>2 candidats minimum</span>
</div>
<FontAwesomeIcon onClick={() => setShow(false)} icon={faTimes} className="mr-2" />
</Alert.Heading>
</Alert>
);
}
return null;
}

@ -1,186 +1,170 @@
import {useState, useEffect} from 'react'
import ButtonWithConfirm from "./ButtonWithConfirm";
import TrashButton from "./TrashButton";
import {
Row,
Col,
Label,
Input,
InputGroup,
InputGroupAddon,
Button, Modal, ModalHeader, ModalBody, Form
} from "reactstrap";
import {useTranslation} from "react-i18next";
import {
sortableHandle
} from "react-sortable-hoc";
import HelpButton from "@components/form/HelpButton";
import AddPicture from "@components/form/AddPicture";
import {
faPlus, faCogs, faCheck, faTrash
} from "@fortawesome/free-solid-svg-icons";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
const DragHandle = sortableHandle(({children}) => (
<span className="input-group-text indexNumber">{children}</span>
));
const CandidateField = ({avatar, label, description, candIndex, onDelete, onAdd, ...inputProps}) => {
const {t} = useTranslation();
const [visibled, setVisibility] = useState(false);
const toggle = () => setVisibility(!visibled)
const [selected, setSelectedState] = useState(false);
const [className, setClassName] = useState("none");
const [trashIcon, setTrashIcon] = useState("none");
const [plusIcon, setPlusIcon] = useState("none");
const addCandidate = () => {
if (label != "") {
toggle();
onAdd();
setSelectedState(!selected);
}
else {}
}
if (label != "") {
const type = "button";
}
else {
const type = "submit";
}
useEffect(() => {
setClassName("candidateButton " + (selected ? "candidateAdded" : ""))
}, [selected]);
useEffect(() => {
setPlusIcon("mr-2 cursorPointer " + (selected ? "trashIcon" : ""))
}, [selected]);
useEffect(() => {
setTrashIcon("trashIcon " + (selected ? "displayTrash" : ""))
}, [selected]);
const addFunction = () => {
addCandidate();
setSelectedState(!selected);
}
const removeCandidate = () => {
onDelete();
toggle();
}
const [image, setImage] = useState(null);
const [createObjectURL, setCreateObjectURL] = useState(null);
const uploadToClient = (event) => {
if (event.target.files && event.target.files[0]) {
const i = event.target.files[0];
setImage(i);
setCreateObjectURL(URL.createObjectURL(i));
}
};
return (
<Row className="rowNoMargin">
<div className={className}>
<div className="avatarThumb">
<img src={createObjectURL} alt="" />
<input placeholder="Ajouter un candidat" className="candidate-placeholder ml-2" value={label} />
</div>
<FontAwesomeIcon onClick={toggle} icon={faPlus} className={plusIcon} />
<div className={trashIcon}><TrashButton label={label} onDelete={onDelete} /></div>
</div>
<Modal
isOpen={visibled}
toggle={toggle}
className="modal-dialog-centered"
>
<ModalHeader className='closeModalAddCandidate' toggle={toggle}>
</ModalHeader>
<ModalBody>
<Col className="addCandidateCard">
<InputGroup className="addCandidateForm">
<Form>
<InputGroupAddon addonType="prepend" className="addCandidateHeader">
<DragHandle>
<h6>Ajouter un participant</h6>
<p>Ajoutez une photo, le nom et une description au candidat.</p>
<div className="ajout-avatar">
<div>
<div className="avatar-placeholer">
<img src={createObjectURL} />
</div>
</div>
<div className="avatar-text">
<h4>Photo <span> (facultatif)</span></h4>
<p>Importer une photo.<br />format : jpg, png, pdf</p>
<div className="btn-ajout-avatar">
<input type="file" name="myImage" id="myImage" value={avatar} onChange={uploadToClient} />
<label className="inputfile" htmlFor="myImage">Importer une photo</label>
</div>
</div>
</div>
<img src="/avatar.svg" />
</DragHandle>
</InputGroupAddon>
<Label className="addCandidateText">Nom et prenom</Label>
<Input
type="text"
value={label}
{...inputProps}
placeholder={t("resource.candidatePlaceholder")}
tabIndex={candIndex + 1}
maxLength="250"
autoFocus
className="addCandidateText"
required
/>
<Label>Description (Facultatif)</Label>
<Input
type="text"
defaultValue={description}
maxLength="250"
/>
<Row className="removeAddButtons">
<ButtonWithConfirm className="removeButton" label={label} onDelete={removeCandidate} />
<Button type={type} className="addButton" label={label} onClick={addCandidate}>
<FontAwesomeIcon icon={faPlus} />
<span>Ajouter</span>
</Button>
</Row>
</Form>
</InputGroup>
</Col>
</ModalBody></Modal>
{/* <Col xs="auto" className="align-self-center pl-0">
<HelpButton>
{t(
"Enter the name of your candidate or proposal here (250 characters max.)"
)}
</HelpButton>
</Col> */}
</Row>
);
// import {useState, useEffect} from 'react'
// import ButtonWithConfirm from "./ButtonWithConfirm";
// import TrashButton from "./TrashButton";
// import {
// Row,
// Col,
// Label,
// Input,
// InputGroup,
// InputGroupAddon,
// Button, Modal, ModalHeader, ModalBody, Form
// } from "reactstrap";
// import {useTranslation} from "react-i18next";
// import HelpButton from "@components/form/HelpButton";
// import AddPicture from "@components/form/AddPicture";
// import {
// faPlus, faCogs, faCheck, faTrash
// } from "@fortawesome/free-solid-svg-icons";
// import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
// const DragHandle = sortableHandle(({children}) => (
// <span className="input-group-text indexNumber">{children}</span>
// ));
// const CandidateField = ({avatar, label, description, candIndex, onDelete, onAdd, ...inputProps}) => {
const CandidateField = (props) => {
// const {t} = useTranslation();
// const [visibled, setVisibility] = useState(false);
// const toggle = () => setVisibility(!visibled)
// const [selected, setSelectedState] = useState(false);
// const [className, setClassName] = useState("none");
// const [trashIcon, setTrashIcon] = useState("none");
// const [plusIcon, setPlusIcon] = useState("none");
// const addCandidate = () => {
// if (label != "") {
// toggle();
// onAdd();
// setSelectedState(!selected);
// }
// else {}
// }
// const type = label != "" ? "button" : "submit";
// useEffect(() => {
// setClassName("candidateButton " + (selected ? "candidateAdded" : ""))
// }, [selected]);
// useEffect(() => {
// setPlusIcon("mr-2 cursorPointer " + (selected ? "trashIcon" : ""))
// }, [selected]);
// useEffect(() => {
// setTrashIcon("trashIcon " + (selected ? "displayTrash" : ""))
// }, [selected]);
// const addFunction = () => {
// addCandidate();
// setSelectedState(!selected);
// }
// const removeCandidate = () => {
// onDelete();
// toggle();
// }
//const [image, setImage] = useState(null);
// const [createObjectURL, setCreateObjectURL] = useState(null);
// const uploadToClient = (event) => {
// if (event.target.files && event.target.files[0]) {
// const i = event.target.files[0];
// setImage(i);
// setCreateObjectURL(URL.createObjectURL(i));
// }
// };
return (<p>FOO</p>);
// return (
// <Row className="rowNoMargin">
// <div className={className}>
// <div className="avatarThumb">
// <img src={createObjectURL} alt="" />
// <input placeholder="Ajouter un candidat" className="candidate-placeholder ml-2" value={label} />
// </div>
//
// <FontAwesomeIcon onClick={toggle} icon={faPlus} className={plusIcon} />
// <div className={trashIcon}><TrashButton label={label} onDelete={onDelete} /></div>
// </div>
//
// <Modal
// isOpen={visibled}
// toggle={toggle}
// className="modal-dialog-centered"
// >
//
// <ModalHeader className='closeModalAddCandidate' toggle={toggle}>
//
// </ModalHeader>
// <ModalBody>
// <Col className="addCandidateCard">
// <InputGroup className="addCandidateForm">
// <Form>
// <InputGroupAddon addonType="prepend" className="addCandidateHeader">
// { //<DragHandle>
// }
// <h6>Ajouter un participant</h6>
// <p>Ajoutez une photo, le nom et une description au candidat.</p>
// <div className="ajout-avatar">
// <div>
// <div className="avatar-placeholer">
// <img src={createObjectURL} />
// </div>
// </div>
// <div className="avatar-text">
// <h4>Photo <span> (facultatif)</span></h4>
//
// <p>Importer une photo.<br />format : jpg, png, pdf</p>
// <div className="btn-ajout-avatar">
// <input type="file" name="myImage" id="myImage" value={avatar} onChange={uploadToClient} />
// <label className="inputfile" htmlFor="myImage">Importer une photo</label>
// </div>
// </div>
// </div>
// <img src="/avatar.svg" />
// { //</InputGroupAddon></DragHandle>
// }
// </InputGroupAddon>
// <Label className="addCandidateText">Nom et prenom</Label>
// <Input
// type="text"
// value={label}
// {...inputProps}
// placeholder={t("resource.candidatePlaceholder")}
// tabIndex={candIndex + 1}
// maxLength="250"
// autoFocus
// className="addCandidateText"
// required
// />
// <Label>Description (Facultatif)</Label>
// <Input
// type="text"
// defaultValue={description}
// maxLength="250"
// />
// <Row className="removeAddButtons">
//
// <ButtonWithConfirm className="removeButton" label={label} onDelete={removeCandidate} />
//
// <Button type={type} className="addButton" label={label} onClick={addCandidate}>
// <FontAwesomeIcon icon={faPlus} />
// <span>Ajouter</span>
// </Button>
//
// </Row>
// </Form>
// </InputGroup>
// </Col>
// </ModalBody></Modal>
// {/* <Col xs="auto" className="align-self-center pl-0">
// <HelpButton>
// {t(
// "Enter the name of your candidate or proposal here (250 characters max.)"
// )}
// </HelpButton>
// </Col> */}
// </Row >
// );
}
export default CandidateField

@ -1,102 +1,116 @@
import {useState, useEffect, createRef} from 'react'
import {useTranslation} from "react-i18next";
import {
Button,
Card,
CardBody
} from "reactstrap";
import {
faPlus,
} from "@fortawesome/free-solid-svg-icons";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {
sortableContainer,
sortableElement,
sortableHandle
} from "react-sortable-hoc";
import arrayMove from "array-move"
// import {DndContext, useDroppable} from '@dnd-kit/core';
import CandidateField from './CandidateField'
import AlertDismissibleExample from './AlertButton'
import Alert from '@components/Alert'
import {MAX_NUM_CANDIDATES} from '@services/constants';
// export function CandidateList(props) {
// const {isOver, setNodeRef} = useDroppable({
// id: props.id,
// });
// const style = {
// opacity: isOver ? 1 : 0.5,
// };
//
// return (
// <div ref={setNodeRef} style={style}>
// {props.children}
// </div>
// );
// }
// const SortableItem = sortableElement(({className, ...childProps}) => <li className={className}><CandidateField {...childProps} /></li>);
//
// const SortableContainer = sortableContainer(({children}) => {
// return <ul className="sortable">{children}</ul>;
// });
const SortableItem = ({className, ...childProps}) => <li className={className}><CandidateField {...childProps} /></li>;
const SortableContainer = ({children}) => {
return <ul className="sortable">{children}</ul>;
};
// const arrayMove = (arr, fromIndex, toIndex) => {
// // https://stackoverflow.com/a/6470794/4986615
// const element = arr[fromIndex];
// arr.splice(fromIndex, 1);
// arr.splice(toIndex, 0, element);
// return arr
// }
const CandidatesField = ({onChange}) => {
const {t} = useTranslation();
const [candidates, setCandidates] = useState([])
const createCandidate = () => ({label: "", description: "", fieldRef: createRef()})
// Initialize the list with at least two candidates
const [candidates, setCandidates] = useState([createCandidate(), createCandidate()])
const [error, setError] = useState(null)
const addCandidate = () => {
if (candidates.length < 1000) {
candidates.push({label: "", description: "", fieldRef: createRef()});
setCandidates([...candidates]);
onChange(candidates)
if (candidates.length < MAX_NUM_CANDIDATES) {
setCandidates(
c => {
c.push(createCandidate());
return c
}
);
} else {
console.error("Too many candidates")
setError('error.too-many-candidates')
}
};
useEffect(() => {
addCandidate();
}, [])
// What to do when we change the candidates
useEffect(() => {
onChange();
}, [candidates])
const removeCandidate = index => {
if (candidates.length === 1) {
const newCandidates = []
newCandidates.push({label: "", fieldRef: createRef()});
setCandidates(newCandidates);
onChange(newCandidates)
setCandidates([createCandidate()]);
}
else {
const newCandidates = candidates.filter((c, i) => i != index)
setCandidates(newCandidates);
onChange(newCandidates);
setCandidates(oldCandidates =>
oldCandidates.filter((_, i) => i != index)
);
}
};
const editCandidate = (index, label) => {
candidates[index].label = label
setCandidates([...candidates]);
onChange(candidates);
setCandidates(
oldCandidates => {
oldCandidates[index].label = label;
return oldCandidates;
}
)
};
const handleKeyPress = (e, index) => {
if (e.key === "Enter") {
if (e.key !== "Enter") {
e.preventDefault();
if (index + 1 === candidates.length) {
addCandidate();
}
else {
}candidates[index + 1].fieldRef.current.focus();
candidates[index + 1].fieldRef.current.focus();
}
}
const onSortEnd = ({oldIndex, newIndex}) => {
setCandidates(arrayMove(candidates, oldIndex, newIndex));
setCandidates(c => arrayMove(c, oldIndex, newIndex));
};
return (
<div className="sectionAjouterCandidat">
<div className="ajouterCandidat">
<h4>Saisissez ici le nom de vos candidats.</h4>
<AlertDismissibleExample />
<SortableContainer onSortEnd={onSortEnd}>
<Alert msg={error} />
{candidates.map((candidate, index) => {
const className = "sortable"
return (
<SortableItem
<CandidateField
className={className}
key={`item-${index}`}
index={index}
@ -111,9 +125,8 @@ const CandidatesField = ({onChange}) => {
/>
)
})}
</SortableContainer>
</div>
</div>
</div >
);
}

@ -10,24 +10,78 @@ const Footer = () => {
const linkStyle = {whiteSpace: "nowrap"};
const {t} = useTranslation();
const [bboxLink1, link1] = useBbox();
const [bboxLink2, link2] = useBbox();
const [bboxLink3, link3] = useBbox();
const [bboxLink4, link4] = useBbox();
const [bboxLink5, link5] = useBbox();
// const [bboxLink1, link1] = useBbox();
// const [bboxLink2, link2] = useBbox();
// const [bboxLink3, link3] = useBbox();
// const [bboxLink4, link4] = useBbox();
// const [bboxLink5, link5] = useBbox();
//<Col className="col-
const menu = [
{
component: (
<Logo title={false} />
)
},
{
component: (
<Link href="/" style={linkStyle}>{t("menu.majority-judgment")}</Link>
)
},
{
component: (
<Link
href="https://mieuxvoter.fr/"
target="_blank"
rel="noopener noreferrer"
style={linkStyle}
>
{t("menu.whoarewe")}
</Link>
)
},
{
component: (
<Link href="/faq" style={linkStyle}>
{t("menu.faq")}
</Link>
)
},
{
component: (
<Link href="/" style={linkStyle}>
{t("menu.news")}
</Link>
)
},
{
component: (
<a href="mailto:app@mieuxvoter.fr?subject=[HELP]" style={linkStyle}>
Nous contacter
</a>
)
},
{
component: (
<div><LanguageSelector /></div>
)
}
]
return (
<footer>
<Row>
<Col className="col-auto me-auto">
<Button className="btn-info">
<a href="/">
Soutenez-nous
</a>
</Button>
<Row>
{menu.map((item, i) =>
<Col key={i} className="col-auto d-flex align-items-center">
{item.component}
</Col>
)}
</Row>
</Col>
<Col className="col-auto ms-auto">
<Col className="col-auto">
<Button className="btn-info">
<a href="/">
Soutenez-nous

@ -1,29 +1,27 @@
/* eslint react/prop-types: 0 */
import { useState } from "react";
import {useState} from "react";
import {
Collapse,
Navbar,
NavbarToggler,
Nav,
NavItem,
Button,
} from "reactstrap";
import Link from "next/link";
import { useTranslation } from "next-i18next";
import {useTranslation} from "next-i18next";
import LanguageSelector from "./LanguageSelector";
import Accordion from "react-bootstrap/Accordion";
const Header = () => {
const [isOpen, setOpen] = useState(false);
const toggle = () => setOpen(!isOpen);
const { t } = useTranslation("common");
const {t} = useTranslation("resource");
return (
<header className="mobile-header">
<Navbar light className="nav-mobile" expand="lg">
<div className="navbar-header">
<div className="navbar-header">
<Button onClick={toggle} className="navbar-toggle pt-0 mt-0">
<img src="/open-menu-icon.svg" alt="" height="50" />
@ -33,10 +31,8 @@ const Header = () => {
<Collapse isOpen={isOpen} navbar>
<Nav className="ml-auto navbar-nav-scroll" navbar>
<div className="d-flex flex-row justify-content-between nav-logo">
<Link href="/">
<a className="navbar-brand navbar-brand-mobile">
<img src="/logos/logo.svg" alt="logo" height="80" />
</a>
<Link href="/" className="navbar-brand navbar-brand-mobile">
<img src="/logos/logo.svg" alt="logo" height="80" />
</Link>
<Button onClick={toggle} className="navbar-toggle navbar-close-button">
@ -46,73 +42,56 @@ const Header = () => {
<div>
<NavItem>
<Link href="/">
<a onClick={toggle} className="navbar-my-link nav-link">
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
Le jugement majoritaire
</a>
</Link>
</NavItem>
<NavItem>
<Link href="/">
<a onClick={toggle} className="navbar-my-link nav-link">
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
Qui sommes-nous ?
</a>
</Link>
</NavItem>
<NavItem>
<Link href="/">
<a onClick={toggle} className="navbar-my-link nav-link">
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
Foire aux questions
</a>
</Link>
</NavItem>
<NavItem>
<Link href="/">
<a onClick={toggle} className="navbar-my-link nav-link">
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
On parle de nous
</a>
</Link>
</NavItem>
<NavItem>
<Link href="/">
<a onClick={toggle} className="navbar-my-link nav-link">
<Link href="/" onClick={toggle} className="navbar-my-link nav-link">
Nous contactez
</a>
</Link>
</NavItem>
<NavItem>
<LanguageSelector style={{ width: "80px" }} />
</NavItem>
<NavItem>
<LanguageSelector style={{width: "80px"}} />
</NavItem>
</div>
<NavItem className="navbar-credits-container">
<Button className="btn-primary btn-nav">
<a href="/">
Soutenez-nous
</a>
</a>
</Button>
<hr />
<div className="navbar-credits sharing sharing-mobile">
<p>Partagez lapplication Mieux voter</p>
<Link href="https://www.facebook.com/mieuxvoter.fr/"><img src="/facebook.svg" className="mr-2" /></Link>
<Link href="https://twitter.com/mieux_voter"><img src="/twitter.svg" className="mr-2" /></Link>
</div>
<div className="d-flex">
<Link href="https://jimmys-box.com/">
<a onClick={toggle} className="navbar-jimmy-link">
développé parJIMMY
</a>
</Link>
<p>Partagez lapplication Mieux voter</p>
<Link href="https://www.facebook.com/mieuxvoter.fr/"><img src="/facebook.svg" className="mr-2" /></Link>
<Link href="https://twitter.com/mieux_voter"><img src="/twitter.svg" className="mr-2" /></Link>
</div>
</NavItem>
</Nav>

@ -20,31 +20,28 @@ const Header = () => {
<Head><title>{t("title")}</title></Head>
<header>
<Navbar color="light" light expand="md">
<Link href="/">
<a className="navbar-brand">
<div className="d-flex flex-row">
<div className="align-self-center">
<img src="/logos/logo-color.svg" alt="logo" height="32" />
</div>
<div className="align-self-center ml-2">
<div className="logo-text">
<h1>
{t("Voting platform")}
<small>{t("Majority Judgment")}</small>
</h1>
</div>
<Link href="/" className="navbar-brand">
<div className="d-flex flex-row">
<div className="align-self-center">
<img src="/logos/logo-color.svg" alt="logo" height="32" />
</div>
<div className="align-self-center ml-2">
<div className="logo-text">
<h1>
{t("Voting platform")}
<small>{t("Majority Judgment")}</small>
</h1>
</div>
</div>
</a>
</div>
</Link>
<NavbarToggler onClick={toggle} />
<Collapse isOpen={isOpen} navbar>
<Nav className="ml-auto" navbar>
<NavItem>
<Link href="/new/">
<a className="text-primary nav-link"> <FontAwesomeIcon icon={faRocket} className="mr-2" />
{t("Start an election")}
</a>
<Link href="/new/" className="text-primary nav-link"> <FontAwesomeIcon icon={faRocket} className="mr-2" />
{t("Start an election")}
</Link>
</NavItem>
<NavItem style={{width: "80px"}}>

@ -19,16 +19,12 @@ const LanguageSelector = () => {
// ["GB", "FR", "ES", "DE", "RU"]
["GB", "FR"]
}
showOptionLabel={true}
selected={localeShort}
selectedSize={15}
optionsSize={22}
showSelectedLabel={true}
showSecondaryOptionLabel={false}
customLabels={{ "GB": "Lang: EN", "FR": "Lang: FR" }}
fullWidth={false}
customLabels={{"GB": "English", "FR": "Francais"}}
className="menu-flags"
selectedSize={14}
selectedSize={14}
/>
);
};

@ -1,10 +1,36 @@
module.exports = {
i18n: {
defaultLocale: "fr",
locales: ["en", "fr"],
},
react: {
useSuspense: false,
wait: true
}
};
// https://www.i18next.com/overview/configuration-options#logging
debug: process.env.NODE_ENV === 'development',
i18n: {
defaultLocale: 'en',
locales: ['en', 'fr'],
},
ns: ["resource"],
defaultNS: "resource",
defaultValue: "__STRING_NOT_TRANSLATED__",
/** To avoid issues when deploying to some paas (vercel...) */
localePath: typeof window === 'undefined' ?
require('path').resolve('./public/locales') : '/locales',
reloadOnPrerender: process.env.NODE_ENV === 'development',
/**
* @link https://github.com/i18next/next-i18next#6-advanced-configuration
*/
// saveMissing: false,
// strictMode: true,
// serializeConfig: false,
// react: { useSuspense: false }
}
// const path = require('path')
// module.exports = {
// i18n: {
// defaultLocale: "fr",
// locales: ["en", "fr"],
// },
// localePath: path.resolve('./public/locales'),
// // react: {
// // useSuspense: false,
// // wait: true
// // }
// };

@ -1,4 +1,4 @@
const {i18n} = require("./next-i18next.config");
const {i18n} = require('./next-i18next.config.js')
module.exports = {

19030
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -10,62 +10,33 @@
"export": "next export"
},
"dependencies": {
"@dnd-kit/core": "^6.0.5",
"@fortawesome/fontawesome-free": "^5.15.3",
"@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-brands-svg-icons": "^5.15.3",
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@fortawesome/react-fontawesome": "^0.1.14",
"@fortawesome/react-fontawesome": "^0.1.19",
"@svgr/webpack": "^6.2.1",
"@weknow/react-bubble-chart-d3": "^1.0.12",
"array-move": "^3.0.1",
"babel-eslint": "^10.1.0",
"bootstrap": "^4.6.0",
"bootstrap-scss": "^4.6.0",
"caniuse-lite": "^1.0.30001423",
"bootstrap": "^5.2.2",
"bootstrap-scss": "^5.2.2",
"clipboard": "^2.0.10",
"d3": "^7.3.0",
"d3-require": "^1.2.4",
"domexception": "^2.0.1",
"dotenv": "^8.6.0",
"embla-carousel-react": "^6.2.0",
"form-data": "^4.0.0",
"framer-motion": "^6.2.8",
"gsap": "^3.9.1",
"handlebars": "^4.7.7",
"highcharts": "^9.3.2",
"eslint-config-next": "^13.0.0",
"highcharts-react-official": "^3.1.0",
"i18next": "^20.2.2",
"i18next-chained-backend": "^2.1.0",
"i18next-fs-backend": "^1.1.1",
"i18next-http-backend": "^1.2.4",
"i18next-localstorage-backend": "^3.1.2",
"i18next-text": "^0.5.6",
"i18next": "^22.0.3",
"mailgun.js": "^3.3.2",
"next": "^12.1.0",
"next-i18next": "^8.2.0",
"query-string": "^7.0.0",
"ramda": "^0.27.2",
"react": "^17.0.2",
"react-bootstrap": "^2.1.0",
"react-bubble-chart": "^0.4.0",
"react-datepicker": "^4.7.0",
"react-dom": "^17.0.2",
"react-flags-select": "^2.1.2",
"react-google-charts": "^3.0.15",
"react-i18next": "^11.8.15",