can submit suggestion

This commit is contained in:
Kalle
2021-02-24 00:10:08 +02:00
parent e787a94de6
commit 271c9ae1fe
4 changed files with 117 additions and 26 deletions

View File

@@ -24,6 +24,7 @@ import {
import * as z from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import UserSelector from "components/common/UserSelector";
import useMutation from "hooks/useMutation";
interface Props {
canVouch: boolean;
@@ -39,10 +40,15 @@ const SuggestionVouchModal: React.FC<Props> = ({
userPlusMembershipTier,
}) => {
const [isOpen, setIsOpen] = useState(false);
const sending = false; //usemutation hook
const { handleSubmit, errors, register, watch, control } = useForm<FormData>({
resolver: zodResolver(suggestionSchema),
});
const { onSubmit, sending } = useMutation({
onClose: () => setIsOpen(false),
route: "plus/suggestions",
mutationKey: "",
successText: t`New suggestion submitted`,
});
const watchDescription = watch("description", "");
@@ -75,7 +81,7 @@ const SuggestionVouchModal: React.FC<Props> = ({
<Trans>Adding a new suggestion or vouch</Trans>
</ModalHeader>
<ModalCloseButton borderRadius="50%" />
<form>
<form onSubmit={handleSubmit(onSubmit)}>
<ModalBody pb={2}>
<FormLabel>
<Trans>Tier</Trans>
@@ -99,21 +105,28 @@ const SuggestionVouchModal: React.FC<Props> = ({
</Select>
)}
/>
<FormLabel mt={4}>
<Trans>User</Trans>
</FormLabel>
<Controller
name="suggestedUserId"
control={control}
render={({ value, onChange }) => (
<UserSelector
value={value}
setValue={onChange}
isMulti={false}
maxMultiCount={undefined}
/>
)}
/>
<FormControl isInvalid={!!errors.suggestedId}>
<FormLabel mt={4}>
<Trans>User</Trans>
</FormLabel>
<Controller
name="suggestedId"
control={control}
render={({ value, onChange }) => (
<UserSelector
value={value}
setValue={onChange}
isMulti={false}
maxMultiCount={undefined}
/>
)}
/>
<FormErrorMessage>
{errors.suggestedId?.message}
</FormErrorMessage>
</FormControl>
<FormControl>
<FormLabel mt={4}>
<Trans>Region</Trans>
@@ -127,6 +140,7 @@ const SuggestionVouchModal: React.FC<Props> = ({
they play most commonly with.
</FormHelperText>
</FormControl>
<FormControl isInvalid={!!errors.description}>
<FormLabel htmlFor="description" mt={4}>
<Trans>Description</Trans>

51
hooks/useMutation.ts Normal file
View File

@@ -0,0 +1,51 @@
import { useToast } from "@chakra-ui/react";
import { getToastOptions } from "lib/getToastOptions";
import { sendData } from "lib/postData";
import { useState } from "react";
import { mutate } from "swr";
import { useUser } from "./common";
const useMutation = ({
route,
mutationKey,
onClose,
successText,
}: {
route: string;
mutationKey: string;
onClose: () => void;
successText: string;
}) => {
const toast = useToast();
const [sending, setSending] = useState(false);
const [loggedInUser] = useUser();
const onSubmit = async (formData: object) => {
if (!loggedInUser) {
console.error("Unexpected no logged in user");
return;
}
setSending(true);
const mutationData = { ...formData };
for (const [key, value] of Object.entries(mutationData)) {
if (value === "" || value === undefined) {
// @ts-ignore
mutationData[key] = null;
}
}
const success = await sendData("POST", "/api/" + route, mutationData);
setSending(false);
if (!success) return;
mutate(mutationKey);
toast(getToastOptions(successText, "success"));
onClose();
};
return { onSubmit, sending };
};
export default useMutation;

View File

@@ -3,7 +3,7 @@ import * as z from "zod";
export const SUGGESTION_DESCRIPTION_LIMIT = 500;
export const suggestionSchema = z.object({
description: z.string().max(SUGGESTION_DESCRIPTION_LIMIT),
description: z.string().max(SUGGESTION_DESCRIPTION_LIMIT).min(10),
suggestedId: z.number().int(),
tier: z.number().int().min(1).max(3),
region: z.enum(["NA", "EU"]),

View File

@@ -108,9 +108,9 @@ const addSuggestion = async ({
userId: number;
}) => {
const parsedData = { ...suggestionSchema.parse(data), suggesterId: userId };
const [suggestions, plusStatus] = await Promise.all([
const [suggestions, plusStatuses] = await Promise.all([
prisma.plusSuggestion.findMany({}),
prisma.plusStatus.findUnique({ where: { userId } }),
prisma.plusStatus.findMany({}),
]);
const existingSuggestion = suggestions.find(
({ tier, suggestedId }) =>
@@ -128,21 +128,47 @@ const addSuggestion = async ({
}
}
const suggesterPlusStatus = plusStatuses.find(
(status) => status.userId === userId
);
if (
!plusStatus ||
!plusStatus.membershipTier ||
plusStatus.membershipTier > parsedData.tier
!suggesterPlusStatus ||
!suggesterPlusStatus.membershipTier ||
suggesterPlusStatus.membershipTier > parsedData.tier
) {
throw new UserError(
"not a member of high enough tier to suggest for this tier"
);
}
if (suggestedUserAlreadyHasAccess()) {
throw new UserError("suggested user already has access");
}
// TODO voting has started
return prisma.plusSuggestion.create({
data: { ...parsedData, isResuggestion: !!existingSuggestion },
});
return prisma.$transaction([
prisma.plusSuggestion.create({
data: { ...parsedData, isResuggestion: !!existingSuggestion },
}),
prisma.plusStatus.upsert({
where: { userId: parsedData.suggestedId },
create: { region: parsedData.region, userId: parsedData.suggestedId },
update: {},
}),
]);
function suggestedUserAlreadyHasAccess() {
const suggestedPlusStatus = plusStatuses.find(
(status) => status.userId === parsedData.suggestedId
);
return Boolean(
suggestedPlusStatus &&
((suggestedPlusStatus.membershipTier ?? 999) <= parsedData.tier ||
(suggestedPlusStatus.vouchTier ?? 999) <= parsedData.tier)
);
}
};
export default {