Front page recent winners initial

This commit is contained in:
Kalle
2022-08-21 22:34:36 +03:00
parent c30b161b73
commit 14d12e110c
4 changed files with 256 additions and 4 deletions

View File

@@ -29,6 +29,7 @@ import findByIdSql from "./findById.sql";
import startTimesOfRangeSql from "./startTimesOfRange.sql";
import findBadgesByEventIdSql from "./findBadgesByEventId.sql";
import eventsToReportSql from "./eventsToReport.sql";
import recentWinnersSql from "./recentWinners.sql";
const createStm = sql.prepare(createSql);
const updateStm = sql.prepare(updateSql);
@@ -231,6 +232,67 @@ export function findResultsByUserId(userId: User["id"]) {
}));
}
const recentWinnersStm = sql.prepare(recentWinnersSql);
export function recentWinners() {
const rows = recentWinnersStm.all() as Array<{
eventId: CalendarEventResultTeam["eventId"];
eventName: CalendarEvent["name"];
startTime: CalendarEventDate["startTime"];
teamName: CalendarEventResultTeam["name"];
playerId: CalendarEventResultPlayer["userId"];
playerName: CalendarEventResultPlayer["name"];
playerDiscordName: User["discordName"] | null;
playerDiscordDiscriminator: User["discordDiscriminator"] | null;
playerDiscordId: User["discordId"] | null;
playerDiscordAvatar: User["discordAvatar"];
}>;
const result: Array<{
eventId: CalendarEventResultTeam["eventId"];
eventName: CalendarEvent["name"];
teamName: CalendarEventResultTeam["name"];
startTime: CalendarEventDate["startTime"];
players: Array<
| string
| Pick<
User,
| "id"
| "discordId"
| "discordName"
| "discordDiscriminator"
| "discordAvatar"
>
>;
}> = [];
for (const row of rows) {
const team = result.find((team) => team.teamName === row.teamName);
const player = row.playerName ?? {
// player name and user id are mutually exclusive
// also if user id exists we know a joined user also has to exist
id: row.playerId!,
discordId: row.playerDiscordId!,
discordName: row.playerDiscordName!,
discordDiscriminator: row.playerDiscordDiscriminator!,
discordAvatar: row.playerDiscordAvatar,
};
if (team) {
team.players.push(player);
} else {
result.push({
eventId: row.eventId,
eventName: row.eventName,
teamName: row.teamName,
startTime: row.startTime,
players: [player],
});
}
}
return result;
}
const findAllBetweenTwoTimestampsStm = sql.prepare(
findAllBetweenTwoTimestampsSql
);

View File

@@ -0,0 +1,34 @@
with "RecentEvents" as (
select
"CalendarEventResultTeam"."eventId",
"CalendarEvent"."name" as "eventName",
"CalendarEventResultTeam"."id" as "teamId",
"CalendarEventDate"."startTime",
"CalendarEventResultTeam"."name" as "teamName"
from
"CalendarEventDate"
inner join "CalendarEventResultTeam" on "CalendarEventResultTeam"."eventId" = "CalendarEventDate"."eventId"
and "CalendarEventResultTeam"."placement" = 1
inner join "CalendarEvent" on "CalendarEvent"."id" = "CalendarEventResultTeam"."eventId"
group by
"CalendarEventDate"."eventId"
order by
"CalendarEventDate"."startTime" desc
limit
3
)
select
"RecentEvents"."eventId",
"RecentEvents"."eventName",
"RecentEvents"."startTime",
"RecentEvents"."teamName",
"CalendarEventResultPlayer"."userId" as "playerId",
"CalendarEventResultPlayer"."name" as "playerName",
"User"."discordName" as "playerDiscordName",
"User"."discordDiscriminator" as "playerDiscordDiscriminator",
"User"."discordId" as "playerDiscordId",
"User"."discordAvatar" as "playerDiscordAvatar"
from
"RecentEvents"
left join "CalendarEventResultPlayer" on "CalendarEventResultPlayer"."teamId" = "RecentEvents"."teamId"
left join "User" on "User"."id" = "CalendarEventResultPlayer"."userId";

View File

@@ -1,11 +1,109 @@
import { json, type LinksFunction } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { Main } from "~/components/Main";
import { db } from "~/db";
import styles from "~/styles/front-page.css";
import { databaseTimestampToDate } from "~/utils/dates";
import { discordFullName } from "~/utils/strings";
import { calendarEventPage, userPage } from "~/utils/urls";
export const links: LinksFunction = () => {
return [{ rel: "stylesheet", href: styles }];
};
export const loader = () => {
return json({
upcomingEvents: [],
recentWinners: db.calendarEvents.recentWinners(),
});
};
export default function Index() {
return (
<Main>
<p className="text-center text-sm">
Welcome to this work in progress Splatoon 3 version of sendou.ink :){" "}
</p>
<Main className="stack md">
<Header />
<CalendarPeek />
</Main>
);
}
function Header() {
return (
<div className="front__logo-container">
<h1>sendou.ink</h1>
<h2>Competitive Splatoon Hub</h2>
</div>
);
}
function CalendarPeek() {
const data = useLoaderData<typeof loader>();
const { i18n } = useTranslation();
return (
<div className="front__calendar-peek-container">
<h2>Recent winners</h2>
{data.recentWinners.map((result) => (
<Event
key={result.eventId}
eventId={result.eventId}
eventName={result.eventName}
startTimeString={databaseTimestampToDate(
result.startTime
).toLocaleDateString(i18n.language, {
day: "numeric",
month: "numeric",
})}
>
<ul className="front__event-winners">
{result.players.map((player) => (
<li
key={typeof player === "string" ? player : player.id}
className="flex items-center"
>
{typeof player === "string" ? (
player
) : (
<Link
to={userPage(player.discordId)}
className="stack horizontal xs items-center"
>
{discordFullName(player)}
</Link>
)}
</li>
))}
</ul>
</Event>
))}
<h2>Upcoming events</h2>
{data.recentWinners.map((result) => (
<div key={result.eventId}>{result.eventName}</div>
))}
</div>
);
}
function Event({
eventId,
eventName,
startTimeString,
children,
}: {
eventId: number;
eventName: string;
startTimeString: string;
children: React.ReactNode;
}) {
return (
<div className="front__event">
<Link to={calendarEventPage(eventId)} className="front__event-name">
{eventName}
</Link>
<div className="front__event-time">{startTimeString}</div>
<div className="front__event-content-below">{children}</div>
</div>
);
}

58
app/styles/front-page.css Normal file
View File

@@ -0,0 +1,58 @@
.front__logo-container {
text-align: center;
}
.front__logo-container > h1 {
font-size: var(--fonts-lg);
}
.front__logo-container > h2 {
color: var(--text-lighter);
font-size: var(--fonts-sm);
}
.front__calendar-peek-container {
display: grid;
grid-auto-flow: column;
grid-template-columns: 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr 1fr;
row-gap: var(--s-2-5);
}
.front__calendar-peek-container > h2 {
font-size: var(--fonts-md);
}
.front__event {
display: grid;
font-size: var(--fonts-sm);
grid-template-areas: "name secondary" "content content";
grid-template-columns: 1fr 1fr;
}
.front__event-name {
color: var(--text);
font-weight: var(--semi-bold);
grid-area: name;
}
.front__event-time {
color: var(--text-lighter);
font-weight: var(--semi-bold);
}
.front__event-secondary {
grid-area: secondary;
}
.front__event-winners {
display: flex;
padding: 0;
font-size: var(--fonts-xxs);
gap: var(--s-3);
list-style: none;
}
.front__event-content-below {
grid-area: content;
}