From 3b5a41699e4e23c57dbf77c36fc95df2d7a77b33 Mon Sep 17 00:00:00 2001 From: manuconcepbrito <manu041196@gmail.com> Date: Fri, 29 Oct 2021 19:02:55 +0200 Subject: [PATCH] Initial create election component --- wahlfang_api/views.py | 3 + wahlfang_web/src/api/management.js | 8 ++ .../src/components/BasicDatePicker.js | 15 ++- wahlfang_web/src/pages/ManagementApp.js | 4 + .../src/pages/management/AddSession.js | 1 - .../src/pages/management/CreateElection.js | 126 ++++++++++++++++++ .../src/pages/management/SessionDetail.js | 9 +- 7 files changed, 157 insertions(+), 9 deletions(-) create mode 100644 wahlfang_web/src/pages/management/CreateElection.js diff --git a/wahlfang_api/views.py b/wahlfang_api/views.py index aefda17..7eb09fc 100644 --- a/wahlfang_api/views.py +++ b/wahlfang_api/views.py @@ -93,6 +93,9 @@ class ManagerElectionView(generics.ListCreateAPIView, generics.DestroyAPIView): election = get_object_or_404(Election, pk=self.request.query_params.get('pk')) return election + def perform_create(self, serializer): + serializer.save(manager=self.request.user) + def get_queryset(self): manager_sesions = self.request.user.sessions.all() elections_manager = Election.objects.filter(session__in=manager_sesions) diff --git a/wahlfang_web/src/api/management.js b/wahlfang_web/src/api/management.js index da82e1c..46879e1 100644 --- a/wahlfang_web/src/api/management.js +++ b/wahlfang_web/src/api/management.js @@ -103,6 +103,14 @@ export const fetchElections = async (sessionId) => { return await response.json() } +export const createElection = async (values) => { + const response = await makeAuthenticatedManagerRequest(managementAPIRoutes.manageElections, 'POST', values); + if (response.status === 201) { + return true; + } else { + throw Error(await response.json()) + } +} export const deleteElection = async (pk) => { const url = `${managementAPIRoutes.manageElections}?pk=${pk}` diff --git a/wahlfang_web/src/components/BasicDatePicker.js b/wahlfang_web/src/components/BasicDatePicker.js index e3f2c15..87e9f87 100644 --- a/wahlfang_web/src/components/BasicDatePicker.js +++ b/wahlfang_web/src/components/BasicDatePicker.js @@ -5,25 +5,28 @@ import LocalizationProvider from '@mui/lab/LocalizationProvider'; import DateTimePicker from '@mui/lab/DateTimePicker'; export default function BasicDatePicker({ - name, form: {setFieldValue}, - field: {value}, + field: {name, value}, ...rest }) { - const [date, setDate] = React.useState(new Date()); + const [date, setDate] = React.useState(new Date()); + // start_date -> Start date (Optional) + let label = name.replace('_', ' ') + label = label.charAt(0).toUpperCase() + label.slice(1) + label = label + ' (Optional)' return ( <LocalizationProvider dateAdapter={AdapterDateFns}> <DateTimePicker - label="Meeting start (optional)" + label={label} clearable autoOk disablePast value={value} onChange={(newValue) => { - setFieldValue("start_date", newValue); + setFieldValue(name, newValue); }} - id="start_date" + id={name} renderInput={(params) => <TextField {...params} style = {{width: '100%'}} diff --git a/wahlfang_web/src/pages/ManagementApp.js b/wahlfang_web/src/pages/ManagementApp.js index be6ff44..d5e3123 100644 --- a/wahlfang_web/src/pages/ManagementApp.js +++ b/wahlfang_web/src/pages/ManagementApp.js @@ -14,6 +14,7 @@ import ManagerAuthenticatedRoute from "../components/ManagerAuthenticatedRoute"; import ManagerSessions from "./management/ManagerSessions" import SessionDetail from "./management/SessionDetail" import AddSession from "./management/AddSession" +import CreateElection from "./management/CreateElection" export default function ManagementApp() { @@ -70,6 +71,9 @@ export default function ManagementApp() { <Route exact path={`${path}/sessions/:id`}> <SessionDetail/> </Route> + <Route exact path={`${path}/election`}> + <CreateElection/> + </Route> <ManagerAuthenticatedRoute> <Route exact path={`${path}/logout`}> <Suspense fallback={<Loading/>}> diff --git a/wahlfang_web/src/pages/management/AddSession.js b/wahlfang_web/src/pages/management/AddSession.js index 8c410f7..4f2b709 100644 --- a/wahlfang_web/src/pages/management/AddSession.js +++ b/wahlfang_web/src/pages/management/AddSession.js @@ -13,7 +13,6 @@ import {useRecoilState} from "recoil"; import {sessionList} from "../../state/management"; -const DATE_FORMAT = 'DD-MM-YYYY HH:mm' export default function AddSession() { const [toggle, setToggle] = useState(false); diff --git a/wahlfang_web/src/pages/management/CreateElection.js b/wahlfang_web/src/pages/management/CreateElection.js new file mode 100644 index 0000000..c81fa3e --- /dev/null +++ b/wahlfang_web/src/pages/management/CreateElection.js @@ -0,0 +1,126 @@ +import React, {useState} from 'react'; +import Layout from "../../components/Layout"; +import { Formik, Form, Field } from 'formik'; +import Collapse from 'react-bootstrap/Collapse'; +import Button from 'react-bootstrap/Button'; +import * as yup from 'yup'; +import {createElection} from "../../api/management"; +import {Redirect, useHistory} from "react-router-dom"; +import moment from "moment"; +import TextField from '@mui/material/TextField'; +import BasicDatePicker from "../../components/BasicDatePicker" +import {useRecoilState} from "recoil"; +import {electionsListManager} from "../../state/management"; + + + +export default function CreateSession() { + const [toggle, setToggle] = useState(false); + const [date, onDateChange] = useState(new Date()); + const [elections, setElections] = useRecoilState(electionsListManager); + + + const handleSubmitAddSessionForm = (values, {setSubmitting}) => { + let moment_date = new moment(values.start_date); + // keepOffset must be true, bug info here https://github.com/moment/moment/issues/947 + values.start_date = moment_date.toISOString(true) + values.end_date = moment_date.toISOString(true) + createElection(values) + .then(res => { + setSubmitting(false) + window.location.assign('/management/sessions'); + // TODO: Find out why recoil state is not reloaded when pushing the history + // history.push("/management/sessions") + }) + .catch(err => { + const err_beauty = JSON.stringify(err, null, 4); + console.log(err_beauty) + setSubmitting(false); + }) + } + + const validationSchema = yup.object({ + title: yup + .string("Election name") + .required('Please provide an election name') + }); + + const CreateElectionForm = () => ( + <div className="p-5"> + <Formik + initialValues={{ title: '', start_date: '', end_date: '', maximum_votes: 0}} + validationSchema={validationSchema} + onSubmit={handleSubmitAddSessionForm} + > + {({ + values, + errors, + touched, + handleChange, + handleBlur, + handleSubmit, + isSubmitting + }) => ( + <form id="add-election-form" onSubmit={(e) => { + e.preventDefault(); + handleSubmit(); + }}> + <h4>Create Election</h4> + <div className="mt-3 form-group"> + <TextField + style ={{width: '100%'}} + id="title" + variant="standard" + name="title" + label="Session's Title" + value={values.title} + onChange={handleChange} + error={errors.title && touched.title && errors.title} + helperText={touched.title && errors.title} + /> + </div> + <div className="mt-3 form-group"> + <Field component={BasicDatePicker} name="start_date" onChange={handleChange}/> + </div> + <div className="mt-3 form-group"> + <Field component={BasicDatePicker} name="end_date" onChange={handleChange}/> + </div> + <div className="mt-3 form-group"> + <TextField + style ={{width: '100%'}} + id="maximumVotes" + type="number" + label="Maximum number of votes" + variant="standard" + name="maximum_votes" + inputProps={{ min: 0 }} + value={values.maximum_votes} + onChange={handleChange} + error={errors.maximum_votes && touched.maximum_votes && errors.maximum_votes} + helperText={touched.maximum_votes && errors.maximum_votes} + /> + </div> + </form> + )} + </Formik> + </div> + ); + + return ( + <Layout title="addSession"> + <div className="row justify-content-center"> + <div className="col-12"> + <div className="card shadow"> + <div className="card-body"> + <CreateElectionForm/> + <div className="d-grid mt-3"> + <button type="submit" id="id_btn_start" className="btn btn-success" form="add-election-form">Create Election</button> + </div> + </div> + </div> + </div> + </div> + </Layout> + ) + +} \ No newline at end of file diff --git a/wahlfang_web/src/pages/management/SessionDetail.js b/wahlfang_web/src/pages/management/SessionDetail.js index 82d9af8..5944465 100644 --- a/wahlfang_web/src/pages/management/SessionDetail.js +++ b/wahlfang_web/src/pages/management/SessionDetail.js @@ -1,6 +1,6 @@ import React, {useEffect} from 'react'; import Layout from "../../components/Layout"; -import {useParams} from "react-router-dom"; +import {useHistory, useParams} from "react-router-dom"; import {useRecoilValue, useSetRecoilState} from "recoil"; import {sessionById, electionsManagerBySessionId, electionsListManager} from "../../state/management" import Button from '@mui/material/Button'; @@ -34,7 +34,8 @@ export default function SessionDetail() { const session = useRecoilValue(sessionById(parseInt(id))) const elections = useRecoilValue(electionsManagerBySessionId(parseInt(id))) const setElectionState = useSetRecoilState(electionsListManager) - console.log(elections) + + const history = useHistory(); const formatDate = (election) => { @@ -50,6 +51,9 @@ export default function SessionDetail() { displayDate = "Needs to be started manually" } return displayDate + } + const handleToCreateElection = () => { + history.push('/management/election') } const handleClickOpen = (e, index) => { e.stopPropagation(); @@ -143,6 +147,7 @@ export default function SessionDetail() { <Button id="add-election-btn" variant="contained" + onClick={handleToCreateElection} > Add Election </Button> -- GitLab