From 9ea79714b575b6bbf3af9c16f14d71b3791b3517 Mon Sep 17 00:00:00 2001 From: Herman Karlsson Date: Thu, 30 Nov 2023 23:35:30 +0100 Subject: [PATCH 1/2] add enpoint for vote counter --- server/actions/votes.go | 21 +++++++++++++++++++++ server/routes.go | 1 + 2 files changed, 22 insertions(+) diff --git a/server/actions/votes.go b/server/actions/votes.go index 1190bfb..a9c1c3e 100644 --- a/server/actions/votes.go +++ b/server/actions/votes.go @@ -197,6 +197,27 @@ func GetVotes(c *gin.Context) { c.JSON(http.StatusOK, response) } +func GetVoteCount(c *gin.Context) { + electionId, err := uuid.FromString(c.Param("id")) + if err != nil { + fmt.Println(err) + c.String(http.StatusBadRequest, util.BadUUIDMessage) + return + } + + db := database.GetDB() + defer database.ReleaseDB() + + var count int64 + if err := db.Model(database.Vote{}).Where("election_id = ?", electionId).Count(&count).Error; err != nil { + fmt.Println(err) + c.String(http.StatusInternalServerError, util.RequestFailedMessage) + return + } + + c.JSON(http.StatusOK, count) +} + // CountVotes calculates the winner of an election using the "Alternativsomröstning" algorithm, // as described in https://styrdokument.datasektionen.se/reglemente (§3.12.7 Urnval) // Expects there to be token candidates for a vacant spot and a blank vote, which it diff --git a/server/routes.go b/server/routes.go index 7e9ecd2..639e259 100644 --- a/server/routes.go +++ b/server/routes.go @@ -52,6 +52,7 @@ func InitRoutes(r *gin.RouterGroup) { auth.GET("/election/:id/has-voted", actions.HasVoted) read.GET("/election/:id/votes", actions.GetVotes) read.GET("/election/:id/count", actions.CountVotesSchultze) + write.GET("/election/:id/vote-count", actions.GetVoteCount) // read.GET("/election/:id/countOld", actions.CountVotes) vote.GET("/election/hashes", actions.GetHashes) From ff464d90d30a2fdbb32821b04497f79916ea7c80 Mon Sep 17 00:00:00 2001 From: Herman Karlsson Date: Thu, 30 Nov 2023 23:36:32 +0100 Subject: [PATCH 2/2] add vote counte ui --- client/src/components/AdminElectionView.tsx | 13 ++++++++++++- client/src/views/admin/EditElection.tsx | 10 +++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/client/src/components/AdminElectionView.tsx b/client/src/components/AdminElectionView.tsx index 71e23b1..34ca4c5 100644 --- a/client/src/components/AdminElectionView.tsx +++ b/client/src/components/AdminElectionView.tsx @@ -4,6 +4,7 @@ import React from "react"; import { Candidate, NullTime } from "../util/ElectionTypes"; import { CandidateList } from "./CandidateList"; import { DateTimeInput } from "./DateTime"; +import Loading from "./Loading"; export interface ElectionFormValues { title: string, @@ -35,7 +36,8 @@ export interface AdminElectionViewProps { userInputError: string | false, openTimeDefault?: NullTime, closeTimeDefault?: NullTime, - submitDisabled?: boolean + submitDisabled?: boolean, + submittedVotes?: number | null, } const useStyles = createStyles((theme) => { @@ -51,6 +53,7 @@ export const AdminElectionView: React.FC = ({ onCloseTimeChanged, onOpenTimeChanged, userInputError, candidates, onCandidateAdded, onCandidateRemoved, onCandidateChanged, openTimeDefault = null, closeTimeDefault = null, submitDisabled = false, + submittedVotes }) => { const { classes } = useStyles(); @@ -115,7 +118,15 @@ export const AdminElectionView: React.FC = ({ {...electionForm.getInputProps("extraMandates")} min={0} /> + +
+ + Submitted votes + + + {submittedVotes ?? "-"} +
diff --git a/client/src/views/admin/EditElection.tsx b/client/src/views/admin/EditElection.tsx index d234127..f73ca36 100644 --- a/client/src/views/admin/EditElection.tsx +++ b/client/src/views/admin/EditElection.tsx @@ -7,15 +7,14 @@ import { Container, createStyles, Center, Button, Modal, Text, Grid } from "@man import { useForm } from "@mantine/form"; import { useDisclosure, useListState } from "@mantine/hooks"; -import { Candidate, CandidateSchema, Election, ElectionResultResponse, ElectionResultResponseSchema, ElectionSchema, NullTime, parseElectionResponse } from "../../util/ElectionTypes"; +import { Candidate, Election, ElectionResultResponse, ElectionResultResponseSchema, ElectionSchema, NullTime, parseElectionResponse } from "../../util/ElectionTypes"; import useAuthorization from "../../hooks/useAuthorization"; import { useNavigate, useParams } from "react-router-dom"; import { useAPIData } from "../../hooks/useAxios"; import { AdminElectionView, ElectionFormValues } from "../../components/AdminElectionView"; import Loading, { Error } from "../../components/Loading"; -import { ClassNames } from "@emotion/react"; import { DisplaySchultzeResult } from "../../components/DisplayResult"; -import { string, z } from "zod"; +import { z } from "zod"; const useStyles = createStyles((theme) => ({ failed: { @@ -46,6 +45,10 @@ const EditElection: React.FC = () => { closeTime: null as NullTime, } }); + const [voteCount, loadingVotes, errorVotes] = useAPIData( + `/api/election/${electionId}/vote-count`, + (data) => z.number().parseAsync(data) + ); const [finalizeModalOpen, { open: openFinalizeModal, @@ -259,6 +262,7 @@ const EditElection: React.FC = () => { userInputError={userInputError} openTimeDefault={electionData.openTime} closeTimeDefault={electionData.closeTime} + submittedVotes={voteCount} />