mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-01 15:48:23 -05:00
can submit suggestion
This commit is contained in:
@@ -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
51
hooks/useMutation.ts
Normal 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;
|
||||
@@ -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"]),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user