mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-07-24 12:11:56 -05:00
suggestions render
This commit is contained in:
parent
271c9ae1fe
commit
ca2bdabf0e
|
|
@ -1,19 +1,131 @@
|
|||
import { Button } from "@chakra-ui/button";
|
||||
import { Box } from "@chakra-ui/layout";
|
||||
import { Box, Center, Divider, Flex, Stack } from "@chakra-ui/layout";
|
||||
import { Radio, RadioGroup } from "@chakra-ui/react";
|
||||
import { Trans } from "@lingui/macro";
|
||||
import MyLink from "components/common/MyLink";
|
||||
import SubText from "components/common/SubText";
|
||||
import UserAvatar from "components/common/UserAvatar";
|
||||
import { usePlus } from "hooks/plus";
|
||||
import { getFullUsername } from "lib/strings";
|
||||
import { Fragment } from "react";
|
||||
import SuggestionVouchModal from "./SuggestionVouchModal";
|
||||
|
||||
export interface PlusHomePageProps {}
|
||||
|
||||
const PlusHomePage: React.FC<PlusHomePageProps> = () => {
|
||||
const {
|
||||
plusStatusData,
|
||||
suggestionsData,
|
||||
ownSuggestion,
|
||||
suggestionsLoading,
|
||||
suggestionCounts,
|
||||
setSuggestionsFilter,
|
||||
} = usePlus();
|
||||
return (
|
||||
<>
|
||||
<SuggestionVouchModal
|
||||
canSuggest={true}
|
||||
canVouch={false}
|
||||
userPlusMembershipTier={1}
|
||||
/>
|
||||
<Box>No suggestions yet for this month</Box>
|
||||
{plusStatusData && plusStatusData.membershipTier && (
|
||||
<SuggestionVouchModal
|
||||
canSuggest={!ownSuggestion}
|
||||
canVouch={!!plusStatusData.canVouchFor}
|
||||
userPlusMembershipTier={plusStatusData.membershipTier}
|
||||
/>
|
||||
)}
|
||||
<Center mt={6}>
|
||||
<RadioGroup
|
||||
defaultValue="ALL"
|
||||
onChange={(value) => {
|
||||
const tier = [null, "ONE", "TWO", "THREE"].indexOf(value as any);
|
||||
setSuggestionsFilter(tier === -1 ? undefined : tier);
|
||||
}}
|
||||
>
|
||||
<Stack spacing={4} direction={["column", "row"]}>
|
||||
<Radio value="ALL">
|
||||
<Trans>
|
||||
All (
|
||||
{suggestionCounts.ONE +
|
||||
suggestionCounts.TWO +
|
||||
suggestionCounts.THREE}
|
||||
)
|
||||
</Trans>
|
||||
</Radio>
|
||||
<Radio value="ONE">
|
||||
<Flex align="center">
|
||||
<SubText mr={2}>+1</SubText> ({suggestionCounts.ONE})
|
||||
</Flex>
|
||||
</Radio>
|
||||
<Radio value="TWO">
|
||||
<Flex align="center">
|
||||
<SubText mr={2}>+2</SubText> ({suggestionCounts.TWO})
|
||||
</Flex>
|
||||
</Radio>
|
||||
<Radio value="THREE">
|
||||
<Flex align="center">
|
||||
<SubText mr={2}>+3</SubText> ({suggestionCounts.THREE})
|
||||
</Flex>
|
||||
</Radio>
|
||||
</Stack>
|
||||
</RadioGroup>
|
||||
</Center>
|
||||
{!suggestionsLoading &&
|
||||
suggestionCounts.ONE + suggestionCounts.TWO + suggestionCounts.THREE ===
|
||||
0 ? (
|
||||
<Box mt={4}>No suggestions yet for this month</Box>
|
||||
) : (
|
||||
<>
|
||||
{suggestionsData.map((suggestion, i) => {
|
||||
return (
|
||||
<Fragment key={suggestion.suggestedUser.id}>
|
||||
<Box as="section" my={8}>
|
||||
<Flex
|
||||
alignItems="center"
|
||||
fontWeight="bold"
|
||||
fontSize="1.25rem"
|
||||
>
|
||||
<UserAvatar user={suggestion.suggestedUser} mr={3} />
|
||||
<MyLink
|
||||
href={`/u/${suggestion.suggestedUser.discordId}`}
|
||||
isColored={false}
|
||||
>
|
||||
{getFullUsername(suggestion.suggestedUser)}
|
||||
</MyLink>
|
||||
</Flex>
|
||||
<SubText ml={2} mt={2}>
|
||||
+{suggestion.tier}
|
||||
</SubText>
|
||||
<Box ml={2} mt={4} fontSize="sm">
|
||||
"{suggestion.description}" -{" "}
|
||||
<MyLink
|
||||
href={`/u/${suggestion.suggesterUser.discordId}`}
|
||||
isColored={false}
|
||||
>
|
||||
{getFullUsername(suggestion.suggesterUser)}
|
||||
</MyLink>
|
||||
</Box>
|
||||
{suggestion.resuggestions?.map((resuggestion) => {
|
||||
return (
|
||||
<Box
|
||||
key={resuggestion.suggesterUser.id}
|
||||
ml={2}
|
||||
mt={4}
|
||||
fontSize="sm"
|
||||
>
|
||||
"{resuggestion.description}" -{" "}
|
||||
<MyLink
|
||||
href={`/u/${resuggestion.suggesterUser.discordId}`}
|
||||
isColored={false}
|
||||
>
|
||||
{getFullUsername(resuggestion.suggesterUser)}
|
||||
</MyLink>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
{i < suggestionsData.length - 1 && <Divider />}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ const SuggestionVouchModal: React.FC<Props> = ({
|
|||
const { onSubmit, sending } = useMutation({
|
||||
onClose: () => setIsOpen(false),
|
||||
route: "plus/suggestions",
|
||||
mutationKey: "",
|
||||
mutationKey: "plus/suggestions",
|
||||
successText: t`New suggestion submitted`,
|
||||
});
|
||||
|
||||
|
|
|
|||
65
hooks/plus.ts
Normal file
65
hooks/plus.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { Unpacked } from "lib/types";
|
||||
import { useState } from "react";
|
||||
import { PlusStatus, Suggestions } from "services/plus";
|
||||
import useSWR from "swr";
|
||||
import { useUser } from "./common";
|
||||
|
||||
export function usePlus() {
|
||||
const [user] = useUser();
|
||||
const [suggestionsFilter, setSuggestionsFilter] = useState<
|
||||
number | undefined
|
||||
>(undefined);
|
||||
|
||||
const { data: plusStatusData } = useSWR<PlusStatus>("/api/plus");
|
||||
const { data: suggestionsData } = useSWR<Suggestions>(
|
||||
"/api/plus/suggestions"
|
||||
);
|
||||
|
||||
const suggestions = suggestionsData ?? [];
|
||||
|
||||
const suggestionDescriptions = suggestions
|
||||
.filter((suggestion) => suggestion.isResuggestion)
|
||||
.reduce(
|
||||
(descriptions: Partial<Record<string, Suggestions>>, suggestion) => {
|
||||
const key = suggestion.suggestedUser.id + "_" + suggestion.tier;
|
||||
if (!descriptions[key]) descriptions[key] = [];
|
||||
|
||||
descriptions[key]!.push(suggestion);
|
||||
|
||||
return descriptions;
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return {
|
||||
plusStatusData,
|
||||
suggestionsData: suggestions
|
||||
.filter((suggestion) => {
|
||||
if (suggestion.isResuggestion) return false;
|
||||
return !suggestionsFilter || suggestion.tier === suggestionsFilter;
|
||||
})
|
||||
.map((suggestion) => ({
|
||||
...suggestion,
|
||||
resuggestions:
|
||||
suggestionDescriptions[
|
||||
suggestion.suggestedUser.id + "_" + suggestion.tier
|
||||
],
|
||||
})),
|
||||
suggestionsLoading: !suggestionsData,
|
||||
suggestionCounts: suggestions.reduce(
|
||||
(counts, suggestion) => {
|
||||
const tierString = [null, "ONE", "TWO", "THREE"][
|
||||
suggestion.tier
|
||||
] as keyof typeof counts;
|
||||
counts[tierString]++;
|
||||
|
||||
return counts;
|
||||
},
|
||||
{ ONE: 0, TWO: 0, THREE: 0 }
|
||||
),
|
||||
ownSuggestion: suggestions.find(
|
||||
(suggestion) => suggestion.suggesterUser.id === user?.id
|
||||
),
|
||||
setSuggestionsFilter,
|
||||
};
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ const useMutation = ({
|
|||
setSending(false);
|
||||
if (!success) return;
|
||||
|
||||
mutate(mutationKey);
|
||||
mutate("/api/" + mutationKey);
|
||||
|
||||
toast(getToastOptions(successText, "success"));
|
||||
onClose();
|
||||
|
|
|
|||
28
pages/api/plus/index.ts
Normal file
28
pages/api/plus/index.ts
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import { getMySession } from "lib/api";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
import plusService from "services/plus";
|
||||
|
||||
const plusHandler = async (req: NextApiRequest, res: NextApiResponse) => {
|
||||
const user = await getMySession(req);
|
||||
|
||||
switch (req.method) {
|
||||
case "GET":
|
||||
await getHandler(req, res);
|
||||
break;
|
||||
default:
|
||||
res.status(405).end();
|
||||
}
|
||||
|
||||
async function getHandler(_req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!user) return res.status(401).end();
|
||||
|
||||
try {
|
||||
res.status(200).json(await plusService.getPlusStatus(user.id));
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
res.status(500).end();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default plusHandler;
|
||||
|
|
@ -11,6 +11,9 @@ const suggestionsHandler = async (
|
|||
const user = await getMySession(req);
|
||||
|
||||
switch (req.method) {
|
||||
case "GET":
|
||||
await getHandler(req, res);
|
||||
break;
|
||||
case "POST":
|
||||
await postHandler(req, res);
|
||||
break;
|
||||
|
|
@ -29,6 +32,7 @@ const suggestionsHandler = async (
|
|||
} else if (e instanceof UserError) {
|
||||
res.status(400).json({ message: e.message });
|
||||
} else {
|
||||
console.error(e.message);
|
||||
res.status(500).end();
|
||||
}
|
||||
|
||||
|
|
@ -37,6 +41,15 @@ const suggestionsHandler = async (
|
|||
|
||||
res.status(200).end();
|
||||
}
|
||||
|
||||
async function getHandler(_req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
res.status(200).json(await plusService.getSuggestions());
|
||||
} catch (e) {
|
||||
console.error(e.message);
|
||||
res.status(500).end();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default suggestionsHandler;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,37 @@ import { userBasicSelection } from "lib/prisma";
|
|||
import { suggestionSchema } from "lib/validators/suggestion";
|
||||
import prisma from "prisma/client";
|
||||
|
||||
export type PlusStatus = Prisma.PromiseReturnType<typeof getPlusStatus>;
|
||||
|
||||
const getPlusStatus = async (userId: number) => {
|
||||
return prisma.plusStatus.findUnique({
|
||||
where: { userId },
|
||||
select: {
|
||||
canVouchAgainAfter: true,
|
||||
vouchTier: true,
|
||||
canVouchFor: true,
|
||||
membershipTier: true,
|
||||
region: true,
|
||||
voucher: { select: userBasicSelection },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export type Suggestions = Prisma.PromiseReturnType<typeof getSuggestions>;
|
||||
|
||||
const getSuggestions = async () => {
|
||||
return prisma.plusSuggestion.findMany({
|
||||
select: {
|
||||
createdAt: true,
|
||||
description: true,
|
||||
isResuggestion: true,
|
||||
tier: true,
|
||||
suggestedUser: { select: userBasicSelection },
|
||||
suggesterUser: { select: userBasicSelection },
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export type VotingSummariesByMonthAndTier = Prisma.PromiseReturnType<
|
||||
typeof getVotingSummariesByMonthAndTier
|
||||
>;
|
||||
|
|
@ -172,6 +203,8 @@ const addSuggestion = async ({
|
|||
};
|
||||
|
||||
export default {
|
||||
getPlusStatus,
|
||||
getSuggestions,
|
||||
getVotingSummariesByMonthAndTier,
|
||||
getMostRecentVotingWithResultsMonth,
|
||||
getDistinctSummaryMonths,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user