mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-04-26 09:20:24 -05:00
154 lines
4.8 KiB
TypeScript
154 lines
4.8 KiB
TypeScript
import type {
|
|
LinksFunction,
|
|
LoaderFunction,
|
|
MetaFunction,
|
|
} from "@remix-run/node";
|
|
import { json } from "@remix-run/node";
|
|
import { Link, useLoaderData } from "@remix-run/react";
|
|
import { lastCompletedVoting } from "~/modules/plus-server";
|
|
import { db } from "~/db";
|
|
import type { PlusVotingResultByMonthYear } from "~/db/models/plusVotes.server";
|
|
import type { PlusVotingResult } from "~/db/types";
|
|
import { roundToTwoDecimalPlaces } from "~/utils/number";
|
|
import { makeTitle } from "~/utils/remix";
|
|
import type { Unpacked } from "~/utils/types";
|
|
import styles from "~/styles/plus-history.css";
|
|
import { discordFullName } from "~/utils/strings";
|
|
import { userPage } from "~/utils/urls";
|
|
import clsx from "clsx";
|
|
import { getUser } from "~/modules/auth";
|
|
|
|
export const links: LinksFunction = () => {
|
|
return [{ rel: "stylesheet", href: styles }];
|
|
};
|
|
|
|
export const meta: MetaFunction = () => {
|
|
const { month, year } = lastCompletedVoting(new Date());
|
|
|
|
return {
|
|
title: makeTitle("Plus Server voting history"),
|
|
description: `Plus Server voting results for ${month + 1}/${year}`,
|
|
};
|
|
};
|
|
|
|
interface PlusVotingResultsLoaderData {
|
|
results?: PlusVotingResultByMonthYear["results"];
|
|
ownScores?: {
|
|
score?: PlusVotingResult["score"];
|
|
tier: PlusVotingResult["tier"];
|
|
passedVoting: PlusVotingResult["passedVoting"];
|
|
}[];
|
|
}
|
|
|
|
export const loader: LoaderFunction = async ({ request }) => {
|
|
const user = await getUser(request);
|
|
const { results, ownScores } = db.plusVotes.resultsByMontYear({
|
|
...lastCompletedVoting(new Date()),
|
|
userId: user?.id,
|
|
});
|
|
|
|
return json<PlusVotingResultsLoaderData>({
|
|
results,
|
|
ownScores: ownScores?.map(scoreForDisplaying),
|
|
});
|
|
};
|
|
|
|
export default function PlusVotingResultsPage() {
|
|
const data = useLoaderData<PlusVotingResultsLoaderData>();
|
|
|
|
const { month, year } = lastCompletedVoting(new Date());
|
|
|
|
return (
|
|
<div className="stack md">
|
|
<h2 className="text-center">
|
|
Voting results for {month + 1}/{year}
|
|
</h2>
|
|
{data.ownScores && data.ownScores.length > 0 ? (
|
|
<ul className="plus-history__own-scores stack sm">
|
|
{data.ownScores.map((result) => (
|
|
<li key={result.tier}>
|
|
You{" "}
|
|
{result.passedVoting ? (
|
|
<span className="plus-history__success">passed</span>
|
|
) : (
|
|
<span className="plus-history__fail">didn't pass</span>
|
|
)}{" "}
|
|
the +{result.tier} voting
|
|
{typeof result.score === "number"
|
|
? `, your score was ${result.score}% (at least 50% required to pass)`
|
|
: ""}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
) : null}
|
|
{!data.ownScores ? (
|
|
<div className="text-center text-sm">
|
|
You weren't in the voting this month.
|
|
</div>
|
|
) : null}
|
|
{data.results ? <Results results={data.results} /> : null}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Results({
|
|
results,
|
|
}: {
|
|
results: NonNullable<PlusVotingResultsLoaderData["results"]>;
|
|
}) {
|
|
return (
|
|
<div>
|
|
<div className="text-xs text-lighter">S = Suggested user</div>
|
|
<div className="stack lg">
|
|
{results.map((tiersResults) => (
|
|
<div className="stack md" key={tiersResults.tier}>
|
|
<h3 className="plus-history__tier-header">
|
|
<span>+{tiersResults.tier}</span>
|
|
</h3>
|
|
{(["passed", "failed"] as const).map((status) => (
|
|
<div key={status} className="plus-history__passed-info-container">
|
|
<h4 className="plus-history__passed-header">
|
|
{status === "passed" ? "Passed" : "Didn't pass"} (
|
|
{tiersResults[status].length})
|
|
</h4>
|
|
{tiersResults[status].map((user) => (
|
|
<Link
|
|
to={userPage(user.discordId)}
|
|
className={clsx("plus-history__user-status", {
|
|
failed: status === "failed",
|
|
})}
|
|
key={user.id}
|
|
>
|
|
{user.wasSuggested ? (
|
|
<span className="plus-history__suggestion-s">S</span>
|
|
) : null}
|
|
{discordFullName(user)}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function databaseAvgToPercentage(score: number) {
|
|
const scoreNormalized = score + 1;
|
|
|
|
return roundToTwoDecimalPlaces((scoreNormalized / 2) * 100);
|
|
}
|
|
|
|
function scoreForDisplaying(
|
|
score: Unpacked<NonNullable<PlusVotingResultByMonthYear["ownScores"]>>
|
|
) {
|
|
const showScore = score.wasSuggested && !score.passedVoting;
|
|
|
|
return {
|
|
tier: score.tier,
|
|
score: showScore ? databaseAvgToPercentage(score.score) : undefined,
|
|
passedVoting: score.passedVoting,
|
|
};
|
|
}
|