From ca2bdabf0e70bb56c23322bb46000bc60a529944 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Wed, 24 Feb 2021 02:30:31 +0200 Subject: [PATCH] suggestions render --- components/plus/PlusHomePage.tsx | 126 +++++++++++++++++++++-- components/plus/SuggestionVouchModal.tsx | 2 +- hooks/plus.ts | 65 ++++++++++++ hooks/useMutation.ts | 2 +- pages/api/plus/index.ts | 28 +++++ pages/api/plus/suggestions.ts | 13 +++ services/plus.ts | 33 ++++++ 7 files changed, 260 insertions(+), 9 deletions(-) create mode 100644 hooks/plus.ts create mode 100644 pages/api/plus/index.ts diff --git a/components/plus/PlusHomePage.tsx b/components/plus/PlusHomePage.tsx index 9ea517eb4..5939ca7ce 100644 --- a/components/plus/PlusHomePage.tsx +++ b/components/plus/PlusHomePage.tsx @@ -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 = () => { + const { + plusStatusData, + suggestionsData, + ownSuggestion, + suggestionsLoading, + suggestionCounts, + setSuggestionsFilter, + } = usePlus(); return ( <> - - No suggestions yet for this month + {plusStatusData && plusStatusData.membershipTier && ( + + )} +
+ { + const tier = [null, "ONE", "TWO", "THREE"].indexOf(value as any); + setSuggestionsFilter(tier === -1 ? undefined : tier); + }} + > + + + + All ( + {suggestionCounts.ONE + + suggestionCounts.TWO + + suggestionCounts.THREE} + ) + + + + + +1 ({suggestionCounts.ONE}) + + + + + +2 ({suggestionCounts.TWO}) + + + + + +3 ({suggestionCounts.THREE}) + + + + +
+ {!suggestionsLoading && + suggestionCounts.ONE + suggestionCounts.TWO + suggestionCounts.THREE === + 0 ? ( + No suggestions yet for this month + ) : ( + <> + {suggestionsData.map((suggestion, i) => { + return ( + + + + + + {getFullUsername(suggestion.suggestedUser)} + + + + +{suggestion.tier} + + + "{suggestion.description}" -{" "} + + {getFullUsername(suggestion.suggesterUser)} + + + {suggestion.resuggestions?.map((resuggestion) => { + return ( + + "{resuggestion.description}" -{" "} + + {getFullUsername(resuggestion.suggesterUser)} + + + ); + })} + + {i < suggestionsData.length - 1 && } + + ); + })} + + )} ); }; diff --git a/components/plus/SuggestionVouchModal.tsx b/components/plus/SuggestionVouchModal.tsx index da6de6d11..13d10aba3 100644 --- a/components/plus/SuggestionVouchModal.tsx +++ b/components/plus/SuggestionVouchModal.tsx @@ -46,7 +46,7 @@ const SuggestionVouchModal: React.FC = ({ const { onSubmit, sending } = useMutation({ onClose: () => setIsOpen(false), route: "plus/suggestions", - mutationKey: "", + mutationKey: "plus/suggestions", successText: t`New suggestion submitted`, }); diff --git a/hooks/plus.ts b/hooks/plus.ts new file mode 100644 index 000000000..100cb789c --- /dev/null +++ b/hooks/plus.ts @@ -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("/api/plus"); + const { data: suggestionsData } = useSWR( + "/api/plus/suggestions" + ); + + const suggestions = suggestionsData ?? []; + + const suggestionDescriptions = suggestions + .filter((suggestion) => suggestion.isResuggestion) + .reduce( + (descriptions: Partial>, 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, + }; +} diff --git a/hooks/useMutation.ts b/hooks/useMutation.ts index ac6439312..9621b640d 100644 --- a/hooks/useMutation.ts +++ b/hooks/useMutation.ts @@ -39,7 +39,7 @@ const useMutation = ({ setSending(false); if (!success) return; - mutate(mutationKey); + mutate("/api/" + mutationKey); toast(getToastOptions(successText, "success")); onClose(); diff --git a/pages/api/plus/index.ts b/pages/api/plus/index.ts new file mode 100644 index 000000000..deaf207d6 --- /dev/null +++ b/pages/api/plus/index.ts @@ -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; diff --git a/pages/api/plus/suggestions.ts b/pages/api/plus/suggestions.ts index b9683db2d..9a81fdab4 100644 --- a/pages/api/plus/suggestions.ts +++ b/pages/api/plus/suggestions.ts @@ -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; diff --git a/services/plus.ts b/services/plus.ts index 53ea020e5..97170c4d1 100644 --- a/services/plus.ts +++ b/services/plus.ts @@ -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; + +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; + +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,