can submit votes

This commit is contained in:
Kalle (Sendou) 2021-03-19 09:40:10 +02:00
parent d0ed62e5a5
commit 4486f317b3
4 changed files with 142 additions and 64 deletions

View File

@ -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 }) {

View File

@ -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 (
<Alert status="success" variant="subtle">
<AlertIcon />
Votes succesfully recorded. Voting ends{" "}
{getVotingRange().endDate.toLocaleString()}.
</Alert>
);
return (
<Box>
{previousUser && (
{previousUser ? (
<Box textAlign="center" mb={6}>
<UserAvatar user={previousUser} isSmall />
<Box my={2} fontSize="sm">
@ -47,55 +61,87 @@ export default function PlusVotingPage() {
{previousUser.score}
</Button>
</Box>
) : (
<Box textAlign="center" mb={14} visibility="hidden">
<Box my={2} fontSize="sm">
asd
</Box>
<Button
borderRadius="50%"
height={10}
width={10}
variant="outline"
colorScheme="theme"
onClick={goBack}
>
{2}
</Button>
</Box>
)}
<Progress value={progress} size="xs" colorScheme="pink" />
<Box mt={6} textAlign="center">
<UserAvatar user={currentUser} size="2xl" mx="auto" />
<Box fontSize="2rem" fontWeight="bold" mt={2}>
{getFullUsername(currentUser)}
</Box>
</Box>
<HStack justify="center" spacing={4} mt={2}>
{currentUser.region === plusStatus.region && (
<PlusVotingButton
number={-2}
onClick={() =>
handleVote({ userId: currentUser.userId, score: -2 })
}
/>
)}
<PlusVotingButton
number={-1}
onClick={() => handleVote({ userId: currentUser.userId, score: -1 })}
/>
<PlusVotingButton
number={1}
onClick={() => handleVote({ userId: currentUser.userId, score: 1 })}
/>
{currentUser.region === plusStatus.region && (
<PlusVotingButton
number={2}
onClick={() => handleVote({ userId: currentUser.userId, score: 2 })}
/>
)}
</HStack>
{currentUser.suggestions && (
<Box mt={5}>
<SubText>Suggestions</SubText>
{currentUser.suggestions.map((suggestion) => {
return (
<Box key={suggestion.suggesterUser.id} mt={4} fontSize="sm">
"{suggestion.description}" -{" "}
{getFullUsername(suggestion.suggesterUser)}
</Box>
);
})}
</Box>
{currentUser && (
<>
<Box mt={6} textAlign="center">
<UserAvatar user={currentUser} size="2xl" mx="auto" />
<Box fontSize="2rem" fontWeight="bold" mt={2}>
{getFullUsername(currentUser)}
</Box>
</Box>
<HStack justify="center" spacing={4} mt={2}>
{currentUser.region === plusStatus.region && (
<PlusVotingButton
number={-2}
onClick={() =>
handleVote({ userId: currentUser.userId, score: -2 })
}
/>
)}
<PlusVotingButton
number={-1}
onClick={() =>
handleVote({ userId: currentUser.userId, score: -1 })
}
/>
<PlusVotingButton
number={1}
onClick={() =>
handleVote({ userId: currentUser.userId, score: 1 })
}
/>
{currentUser.region === plusStatus.region && (
<PlusVotingButton
number={2}
onClick={() =>
handleVote({ userId: currentUser.userId, score: 2 })
}
/>
)}
</HStack>
{currentUser.suggestions && (
<Box mt={5}>
<SubText>Suggestions</SubText>
{currentUser.suggestions.map((suggestion) => {
return (
<Box key={suggestion.suggesterUser.id} mt={4} fontSize="sm">
"{suggestion.description}" -{" "}
{getFullUsername(suggestion.suggesterUser)}
</Box>
);
})}
</Box>
)}
{currentUser.bio && (
<Box mt={4}>
<SubText mb={4}>Bio</SubText>
<Markdown value={currentUser.bio} smallHeaders />
</Box>
)}
</>
)}
{currentUser.bio && (
<Box mt={4}>
<SubText mb={4}>Bio</SubText>
<Markdown value={currentUser.bio} smallHeaders />
{previousUser && !currentUser && (
<Box onClick={submit} mt={6} textAlign="center">
{" "}
<Button isLoading={status === "loading"}>Submit</Button>
</Box>
)}
</Box>

View File

@ -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<z.infer<typeof votesSchema>>([]);
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<z.infer<typeof votesSchema>>) => {
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,
};
}

View File

@ -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,
};