From 14d12e110cb9d59970c86f764a83ff0e52bc170f Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 21 Aug 2022 22:34:36 +0300 Subject: [PATCH] Front page recent winners initial --- app/db/models/calendar/queries.server.ts | 62 +++++++++++++ app/db/models/calendar/recentWinners.sql | 34 ++++++++ app/routes/index.tsx | 106 ++++++++++++++++++++++- app/styles/front-page.css | 58 +++++++++++++ 4 files changed, 256 insertions(+), 4 deletions(-) create mode 100644 app/db/models/calendar/recentWinners.sql create mode 100644 app/styles/front-page.css diff --git a/app/db/models/calendar/queries.server.ts b/app/db/models/calendar/queries.server.ts index 24a3ea899..c41c56661 100644 --- a/app/db/models/calendar/queries.server.ts +++ b/app/db/models/calendar/queries.server.ts @@ -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 ); diff --git a/app/db/models/calendar/recentWinners.sql b/app/db/models/calendar/recentWinners.sql new file mode 100644 index 000000000..6da5180c8 --- /dev/null +++ b/app/db/models/calendar/recentWinners.sql @@ -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"; \ No newline at end of file diff --git a/app/routes/index.tsx b/app/routes/index.tsx index b10ea429a..4ae82eadf 100644 --- a/app/routes/index.tsx +++ b/app/routes/index.tsx @@ -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 ( -
-

- Welcome to this work in progress Splatoon 3 version of sendou.ink :){" "} -

+
+
+
); } + +function Header() { + return ( +
+

sendou.ink

+

Competitive Splatoon Hub

+
+ ); +} + +function CalendarPeek() { + const data = useLoaderData(); + const { i18n } = useTranslation(); + + return ( +
+

Recent winners

+ {data.recentWinners.map((result) => ( + +
    + {result.players.map((player) => ( +
  • + {typeof player === "string" ? ( + player + ) : ( + + {discordFullName(player)} + + )} +
  • + ))} +
+
+ ))} +

Upcoming events

+ {data.recentWinners.map((result) => ( +
{result.eventName}
+ ))} +
+ ); +} + +function Event({ + eventId, + eventName, + startTimeString, + children, +}: { + eventId: number; + eventName: string; + startTimeString: string; + children: React.ReactNode; +}) { + return ( +
+ + {eventName} + +
{startTimeString}
+
{children}
+
+ ); +} diff --git a/app/styles/front-page.css b/app/styles/front-page.css new file mode 100644 index 000000000..3753d7c6a --- /dev/null +++ b/app/styles/front-page.css @@ -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; +}