diff --git a/package.json b/package.json
index 288d6ea..fcb9018 100644
--- a/package.json
+++ b/package.json
@@ -16,6 +16,8 @@
"react-dom": "^16.8.6",
"react-router-dom": "^5.0.0",
"react-scripts": "3.0.1",
+ "react-sortable-hoc": "^1.9.1",
+ "react-toastify": "^5.2.1",
"reactstrap": "^8.0.0"
},
"scripts": {
diff --git a/src/components/form/ButtonWithConfirm.js b/src/components/form/ButtonWithConfirm.js
new file mode 100644
index 0000000..dab0412
--- /dev/null
+++ b/src/components/form/ButtonWithConfirm.js
@@ -0,0 +1,53 @@
+import React, {Component} from "react";
+import ModalConfirm from "./ModalConfirm";
+
+
+class ButtonWithConfirm extends Component {
+ constructor(props) {
+ super(props);
+ this._modalConfirm=React.createRef();
+ this.state={
+ focused:false
+ }
+ }
+
+ getComponent= (key) => {
+ return this.props.children.filter( (comp) => {
+ return comp.key === key;
+ });
+ };
+
+ render() {
+ const classNames=this.props.className.split(" ");
+
+ let classNameForDiv="";
+ let classNameForButton="";
+ classNames.forEach(function(className){
+ if(className==="input-group-prepend" || className==="input-group-append" ){
+ classNameForDiv+=" "+className;
+ }else{
+ classNameForButton+=" "+className;
+ }
+ });
+
+
+
+ return (
+
+
+
+ {this.getComponent("modal-title")}
+ {this.getComponent("modal-body")}
+ {this.getComponent("modal-confirm")}
+ {this.getComponent("modal-cancel")}
+
+
+ );
+ }
+}
+
+export default ButtonWithConfirm;
\ No newline at end of file
diff --git a/src/components/form/ModalConfirm.js b/src/components/form/ModalConfirm.js
new file mode 100644
index 0000000..cd69311
--- /dev/null
+++ b/src/components/form/ModalConfirm.js
@@ -0,0 +1,40 @@
+import React, {Component} from "react";
+import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
+
+class ModalConfirm extends Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ modal: false
+ };
+ }
+
+ toggle = () => {
+ this.setState({
+ modal: !this.state.modal
+ });
+ };
+
+ getComponent= (key) => {
+ return this.props.children.filter( (comp) => {
+ return comp.key === key;
+ });
+ };
+
+ render() {
+ return (
+
+ {this.getComponent("title")}
+
+ {this.getComponent("body")}
+
+
+ {' '}
+
+
+
+ );
+ }
+}
+
+export default ModalConfirm;
\ No newline at end of file
diff --git a/src/components/views/CreateBallot.js b/src/components/views/CreateBallot.js
index 6e1f764..362696a 100644
--- a/src/components/views/CreateBallot.js
+++ b/src/components/views/CreateBallot.js
@@ -1,24 +1,108 @@
import React, {Component} from "react";
-import { Container, Row, Col, Input, Label } from 'reactstrap';
+import { Container, Row, Col, Input, Label, Card, CardHeader, CardBody, Collapse } from 'reactstrap';
+import {toast, ToastContainer} from 'react-toastify';
import HelpButton from "../form/HelpButton";
+import {arrayMove, sortableContainer, sortableElement, sortableHandle} from 'react-sortable-hoc';
+import ButtonWithConfirm from "../form/ButtonWithConfirm";
+const DragHandle = sortableHandle(({children}) => {children});
+
+const SortableCandidate = sortableElement(({candidate, sortIndex, form}) =>
+
+
+
+
+
+
+ {sortIndex + 1}
+
+
+
form.editCandidateLabel(event, sortIndex)}
+ maxLength="250"/>
+
+
+ Suppression ?
+ Êtes-vous sûr de vouloir supprimer la
+ proposition "{candidate.label}" ?
+
+ form.removeCandidate(sortIndex)}>Oui
+ Non
+
+
+
+
+
+);
+
+const SortableCandidatesContainer = sortableContainer(({items, form}) => {
+ return {items.map((candidate, index) => (
+
+ ))}
;
+});
class CreateBallot extends Component {
constructor(props) {
super(props);
this.state = {
- }
+ candidates:[]
+ };
+ this._candidateLabelInput = React.createRef();
+ this._addCandidateButton = React.createRef();
+
this.focusInput= React.createRef();
}
+ addCandidate = (evt) => {
+ if (evt.type === "click" || (evt.type === "keydown" && evt.keyCode === 13)) {
+ const candidateFieldLabel = this._candidateLabelInput.current.value;
+ let candidates = this.state.candidates;
+ if (candidates.length < 100) {
+ candidates.push({label: candidateFieldLabel});
+ this._candidateLabelInput.current.value = '';
+ this.setState({isAddCandidateOpen: false, candidates: candidates});
+ }
+
+ }
+
+ };
+
+ removeCandidate = (index) => {
+ let candidates = this.state.candidates;
+ candidates.splice(index, 1);
+ this.setState({candidates: candidates});
+ };
+
+ editCandidateLabel = (event, index) => {
+ let candidates = this.state.candidates;
+ candidates[index].label = event.currentTarget.value;
+ this.setState({candidates: candidates});
+ };
+
+ toggleAddCandidate = () => {
+ if (this.state.candidates.length >= 100) {
+ toast.error("Vous ne pouvez plus ajouter de proposition ! (100 max.)", {
+ position: toast.POSITION.TOP_CENTER
+ });
+ } else {
+ this._candidateLabelInput.current.value = "";
+ this.setState({
+ isAddCandidateOpen: !this.state.isAddCandidateOpen
+ });
+ }
+
+
+ };
+
render(){
const params = new URLSearchParams(this.props.location.search);
return(
+
)
diff --git a/yarn.lock b/yarn.lock
index a8c4731..c361fe0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2556,7 +2556,7 @@ class-utils@^0.3.5:
isobject "^3.0.0"
static-extend "^0.1.1"
-classnames@^2.2.3:
+classnames@^2.2.3, classnames@^2.2.6:
version "2.2.6"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.6.tgz#43935bffdd291f326dad0a205309b38d00f650ce"
integrity sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==
@@ -8443,7 +8443,7 @@ prop-types-exact@^1.2.0:
object.assign "^4.1.0"
reflect.ownkeys "^0.2.0"
-prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
+prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -8788,6 +8788,15 @@ react-scripts@3.0.1:
optionalDependencies:
fsevents "2.0.6"
+react-sortable-hoc@^1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-1.9.1.tgz#ae3d28c3cff87fb862be3ddcde9c76b5b5bd2152"
+ integrity sha512-2VeofjRav8+eZeE5Nm/+b8mrA94rQ+gBsqhXi8pRBSjOWNqslU3ZEm+0XhSlfoXJY2lkgHipfYAUuJbDtCixRg==
+ dependencies:
+ "@babel/runtime" "^7.2.0"
+ invariant "^2.2.4"
+ prop-types "^15.5.7"
+
react-test-renderer@^16.0.0-0:
version "16.8.6"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.8.6.tgz#188d8029b8c39c786f998aa3efd3ffe7642d5ba1"
@@ -8798,7 +8807,17 @@ react-test-renderer@^16.0.0-0:
react-is "^16.8.6"
scheduler "^0.13.6"
-react-transition-group@^2.3.1:
+react-toastify@^5.2.1:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-5.2.1.tgz#faa05bc4cd5066ee50bf56c7f8b8fd1492e71aca"
+ integrity sha512-OEZQld/jvjFCQnmXShb73dxVgslEuVz6Jb9/K22x+OcpQH5abtb278tO+Z9FwWsnu8aOvKiPuEYRrSfXC0HF8w==
+ dependencies:
+ "@babel/runtime" "^7.4.2"
+ classnames "^2.2.6"
+ prop-types "^15.7.2"
+ react-transition-group "^2.6.1"
+
+react-transition-group@^2.3.1, react-transition-group@^2.6.1:
version "2.9.0"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.9.0.tgz#df9cdb025796211151a436c69a8f3b97b5b07c8d"
integrity sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==