diff --git a/app/plus/api.ts b/app/plus/api.ts index 1b5ecdf1f..7f0791a4f 100644 --- a/app/plus/api.ts +++ b/app/plus/api.ts @@ -22,6 +22,12 @@ const plusApi = createRouter() return service.getUsersForVoting(user.id); }, }) + .query("hasVoted", { + resolve({ ctx }) { + const user = throwIfNotLoggedIn(ctx.user); + return service.hasVoted(user.id); + }, + }) .mutation("suggestion", { input: suggestionFullSchema, resolve({ input, ctx }) { diff --git a/app/plus/components/PlusVotingPage.tsx b/app/plus/components/PlusVotingPage.tsx index 90cffc322..c98bf81f0 100644 --- a/app/plus/components/PlusVotingPage.tsx +++ b/app/plus/components/PlusVotingPage.tsx @@ -1,3 +1,4 @@ +import { Alert, AlertIcon } from "@chakra-ui/alert"; import { Button } from "@chakra-ui/button"; import { Box, HStack } from "@chakra-ui/layout"; import { Progress } from "@chakra-ui/progress"; @@ -5,6 +6,7 @@ import Markdown from "components/common/Markdown"; import SubText from "components/common/SubText"; import UserAvatar from "components/common/UserAvatar"; import { useEffect } from "react"; +import { getVotingRange } from "utils/plus"; import { getFullUsername } from "utils/strings"; import usePlusVoting from "../hooks/usePlusVoting"; import { PlusVotingButton } from "./PlusVotingButton"; @@ -19,17 +21,29 @@ export default function PlusVotingPage() { progress, previousUser, goBack, + submit, + status, + hasVoted, } = usePlusVoting(); useEffect(() => { //redirect!! }, [shouldRedirect]); - if (isLoading || !plusStatus || !currentUser) return null; + if (isLoading || !plusStatus) return null; + + if (hasVoted) + return ( + + + Votes succesfully recorded. Voting ends{" "} + {getVotingRange().endDate.toLocaleString()}. + + ); return ( - {previousUser && ( + {previousUser ? ( @@ -47,55 +61,87 @@ export default function PlusVotingPage() { {previousUser.score} + ) : ( + + + asd + + + )} - - - - {getFullUsername(currentUser)} - - - - {currentUser.region === plusStatus.region && ( - - handleVote({ userId: currentUser.userId, score: -2 }) - } - /> - )} - handleVote({ userId: currentUser.userId, score: -1 })} - /> - handleVote({ userId: currentUser.userId, score: 1 })} - /> - {currentUser.region === plusStatus.region && ( - handleVote({ userId: currentUser.userId, score: 2 })} - /> - )} - - {currentUser.suggestions && ( - - Suggestions - {currentUser.suggestions.map((suggestion) => { - return ( - - "{suggestion.description}" -{" "} - {getFullUsername(suggestion.suggesterUser)} - - ); - })} - + {currentUser && ( + <> + + + + {getFullUsername(currentUser)} + + + + {currentUser.region === plusStatus.region && ( + + handleVote({ userId: currentUser.userId, score: -2 }) + } + /> + )} + + handleVote({ userId: currentUser.userId, score: -1 }) + } + /> + + handleVote({ userId: currentUser.userId, score: 1 }) + } + /> + {currentUser.region === plusStatus.region && ( + + handleVote({ userId: currentUser.userId, score: 2 }) + } + /> + )} + + {currentUser.suggestions && ( + + Suggestions + {currentUser.suggestions.map((suggestion) => { + return ( + + "{suggestion.description}" -{" "} + {getFullUsername(suggestion.suggesterUser)} + + ); + })} + + )} + {currentUser.bio && ( + + Bio + + + )} + )} - {currentUser.bio && ( - - Bio - + {previousUser && !currentUser && ( + + {" "} + )} diff --git a/app/plus/hooks/usePlusVoting.ts b/app/plus/hooks/usePlusVoting.ts index c5b7e4c98..1a1e87d7d 100644 --- a/app/plus/hooks/usePlusVoting.ts +++ b/app/plus/hooks/usePlusVoting.ts @@ -1,5 +1,7 @@ +import { useToast } from "@chakra-ui/toast"; import { useUser } from "hooks/common"; import { useState } from "react"; +import { getToastOptions } from "utils/getToastOptions"; import { trpc } from "utils/trpc"; import { Unpacked } from "utils/types"; import { votesSchema } from "utils/validators/votes"; @@ -9,29 +11,41 @@ export default function usePlusVoting() { const [currentIndex, setCurrentIndex] = useState(0); const [votes, setVotes] = useState>([]); + const toast = useToast(); const [user] = useUser(); - const { data: ballotsData, isLoading: isLoadingBallots } = trpc.useQuery([ + + const { data: hasVoted, isLoading: hasVotedIsLoading } = trpc.useQuery([ + "plus.hasVoted", + ]); + const { data: usersForVoting, isLoading: isLoadingBallots } = trpc.useQuery([ "plus.usersForVoting", ]); - const { data: statusesData, isLoading: isLoadingStatuses } = trpc.useQuery([ + const { data: statuses, isLoading: isLoadingStatuses } = trpc.useQuery([ "plus.statuses", ]); + const { mutate, status } = trpc.useMutation("plus.vote", { + onSuccess() { + toast(getToastOptions("Successfully voted", "success")); + trpc.invalidateQuery(["plus.hasVoted"]); + }, + onError(error) { + toast(getToastOptions(error.message, "error")); + }, + }); - const ownPlusStatus = statusesData?.find( - (status) => status.user.id === user?.id - ); + const ownPlusStatus = statuses?.find((status) => status.user.id === user?.id); return { - isLoading: isLoadingBallots || isLoadingStatuses, - shouldRedirect: !isLoadingBallots && !ballotsData, + isLoading: isLoadingBallots || isLoadingStatuses || hasVotedIsLoading, + shouldRedirect: !isLoadingBallots && !usersForVoting, plusStatus: ownPlusStatus, - currentUser: ballotsData?.[currentIndex], + currentUser: usersForVoting?.[currentIndex], previousUser: - currentIndex > 0 && ballotsData - ? { ...ballotsData[currentIndex - 1], ...votes[votes.length - 1] } + currentIndex > 0 && usersForVoting + ? { ...usersForVoting[currentIndex - 1], ...votes[votes.length - 1] } : undefined, - progress: ballotsData - ? ((currentIndex + 1) / ballotsData.length) * 100 + progress: usersForVoting + ? (currentIndex / usersForVoting.length) * 100 : undefined, handleVote: (vote: Unpacked>) => { setVotes([...votes, vote]); @@ -42,5 +56,8 @@ export default function usePlusVoting() { setVotes(votes.slice(0, votes.length - 1)); setCurrentIndex(currentIndex - 1); }, + submit: () => mutate(votes), + status, + hasVoted, }; } diff --git a/app/plus/service.ts b/app/plus/service.ts index 03236a221..34207b48d 100644 --- a/app/plus/service.ts +++ b/app/plus/service.ts @@ -261,6 +261,14 @@ const getUsersForVoting = async (userId: number) => { return shuffleArray(result).sort((a, b) => a.region.localeCompare(b.region)); }; +const hasVoted = async (userId: number) => { + return Boolean( + await prisma.plusBallot.count({ + where: { voterId: userId, isStale: false }, + }) + ); +}; + const addSuggestion = async ({ input, userId, @@ -443,15 +451,15 @@ const addVotes = async ({ if ( input.some((vote) => { const region = allowedUsers.get(vote.userId); - if (!region) return false; + if (!region) return true; if (region === usersPlusStatus.region) { - if (![-2, -1, 1, 2].includes(vote.score)) return false; + if (![-2, -1, 1, 2].includes(vote.score)) return true; } else { - if (![-1, 1].includes(vote.score)) return false; + if (![-1, 1].includes(vote.score)) return true; } - return true; + return false; }) ) { throw httpError.badRequest("invalid vote provided"); @@ -479,4 +487,5 @@ export default { addSuggestion, addVouch, addVotes, + hasVoted, };