sendou.ink/pages/plus/index.tsx
Kalle (Sendou) 7b67a96b30 Closes #625
2021-09-18 18:05:33 +03:00

273 lines
8.5 KiB
TypeScript

import { Box, Center, Divider, Flex, Heading, Stack } from "@chakra-ui/layout";
import {
Alert,
AlertDescription,
AlertTitle,
Progress,
Radio,
RadioGroup,
} from "@chakra-ui/react";
import { Trans } from "@lingui/macro";
import MyHead from "components/common/MyHead";
import SubText from "components/common/SubText";
import Suggestion from "components/plus/Suggestion";
import SuggestionModal from "components/plus/SuggestionModal";
import VotingInfoHeader from "components/plus/VotingInfoHeader";
import VouchesList from "components/plus/VouchesList";
import VouchModal from "components/plus/VouchModal";
import { useUser } from "hooks/common";
import { usePlusHomePage } from "hooks/plus";
import { Fragment } from "react";
import { getVotingRange } from "utils/plus";
import { getFullUsername } from "utils/strings";
import plusService, { PlusStatuses, VotingProgress } from "services/plus";
import { Serialized } from "utils/types";
import { serializeDataForGetStaticProps } from "utils/objects";
const PlusHomePage = ({
statuses,
votingProgress,
}: {
statuses: Serialized<PlusStatuses>;
votingProgress: VotingProgress;
}) => {
const [user] = useUser();
const {
plusStatusData,
plusStatusDataLoading,
vouchStatuses,
suggestionsData,
suggestionsLoading,
ownSuggestion,
suggestionCounts,
setSuggestionsFilter,
vouchedPlusStatusData,
} = usePlusHomePage(statuses);
if (plusStatusDataLoading) return null;
if (!plusStatusData?.membershipTier) {
return (
<Box>
<Box fontSize="sm" mb={4}>
<VotingInfoHeader isMember={!!plusStatusData?.membershipTier} />
</Box>
<Heading size="md">Suggested players this month:</Heading>
<Flex flexWrap="wrap" data-cy="alt-suggestions-container">
{suggestionsData
.sort(
(a, b) =>
new Date(a.createdAt).getTime() -
new Date(b.createdAt).getTime()
)
.map((suggestion) => (
<Box
key={suggestion.tier + "+" + suggestion.suggestedUser.id}
m={1}
>
{getFullUsername(suggestion.suggestedUser)} (+{suggestion.tier})
</Box>
))}
</Flex>
</Box>
);
}
return (
<>
<MyHead title="Plus Server" />
<Box fontSize="sm" mb={4}>
<VotingInfoHeader isMember={!!plusStatusData?.membershipTier} />
</Box>
{votingProgress && (
<Box textAlign="center">
<SubText>
+1 ({votingProgress[1].voted}/{votingProgress[1].totalVoterCount})
</SubText>
<Progress
value={
(votingProgress[1].voted / votingProgress[1].totalVoterCount) *
100
}
size="xs"
colorScheme="pink"
mb={6}
/>
<SubText>
+2 ({votingProgress[2].voted}/{votingProgress[2].totalVoterCount})
</SubText>
<Progress
value={
(votingProgress[2].voted / votingProgress[2].totalVoterCount) *
100
}
size="xs"
colorScheme="blue"
mb={6}
/>
<SubText>
+3 ({votingProgress[3].voted}/{votingProgress[3].totalVoterCount})
</SubText>
<Progress
value={
(votingProgress[3].voted / votingProgress[3].totalVoterCount) *
100
}
size="xs"
colorScheme="yellow"
mb={6}
/>
</Box>
)}
{!getVotingRange().isHappening && (
<>
{plusStatusData &&
plusStatusData.membershipTier &&
!ownSuggestion && (
<SuggestionModal
userPlusMembershipTier={plusStatusData.membershipTier}
/>
)}
{plusStatusData &&
plusStatusData.canVouchFor &&
!plusStatusData.canVouchAgainAfter && (
<VouchModal canVouchFor={plusStatusData.canVouchFor} />
)}
</>
)}
{plusStatusData &&
(plusStatusData.canVouchAgainAfter ||
plusStatusData.voucher ||
vouchedPlusStatusData) && (
<Alert
status="success"
variant="subtle"
flexDirection="column"
alignItems="center"
justifyContent="center"
textAlign="center"
mt={2}
mb={6}
rounded="lg"
>
<AlertDescription maxWidth="sm">
<AlertTitle mb={1} fontSize="lg">
Vouching status
</AlertTitle>
{plusStatusData?.canVouchAgainAfter && (
<Box>
Can vouch again after:{" "}
{new Date(
// @ts-expect-error TODO: make Serialized<T> handle null union
plusStatusData.canVouchAgainAfter
).toLocaleDateString()}{" "}
(resets after voting)
</Box>
)}
{plusStatusData?.voucher && (
<Box>
Vouched for <b>+{plusStatusData.vouchTier}</b> by{" "}
{getFullUsername(plusStatusData.voucher)}
</Box>
)}
{vouchedPlusStatusData && (
<Box>
Vouched {getFullUsername(vouchedPlusStatusData.user)} to{" "}
<b>+{vouchedPlusStatusData.vouchTier}</b>
</Box>
)}
</AlertDescription>
</Alert>
)}
<Center mt={2}>
<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" data-cy="plus-three-radio">
<Flex align="center">
<SubText mr={2}>+3</SubText> ({suggestionCounts.THREE})
</Flex>
</Radio>
</Stack>
</RadioGroup>
</Center>
{!getVotingRange().isHappening &&
vouchStatuses &&
vouchStatuses.length > 0 && (
<Box mt={4}>
<VouchesList vouches={vouchStatuses} />
</Box>
)}
{suggestionCounts.ONE + suggestionCounts.TWO + suggestionCounts.THREE ===
0 && !suggestionsLoading ? (
<Box mt={4}>No suggestions yet for this month</Box>
) : (
<>
{suggestionsData.map((suggestion, i) => {
const canSuggest = () => {
if (!plusStatusData?.membershipTier) return false;
if (plusStatusData.membershipTier > suggestion.tier) return false;
if (suggestion.suggesterUser.id === user?.id) return false;
if (
suggestion.resuggestions?.some(
(suggestion) => suggestion.suggesterUser.id === user?.id
)
)
return false;
return true;
};
return (
<Fragment
key={suggestion.suggestedUser.id + "-" + suggestion.tier}
>
<Suggestion suggestion={suggestion} canSuggest={canSuggest()} />
{i < suggestionsData.length - 1 && <Divider />}
</Fragment>
);
})}
</>
)}
</>
);
};
export const getStaticProps = async () => {
const [statuses, votingProgress] = await Promise.all([
plusService.getPlusStatuses(),
getVotingRange().isHappening ? plusService.votingProgress() : null,
]);
return {
props: {
statuses: serializeDataForGetStaticProps(statuses),
votingProgress: serializeDataForGetStaticProps(votingProgress),
},
revalidate: 60,
};
};
export default PlusHomePage;