mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-27 13:45:27 -05:00
Front page recent winners initial
This commit is contained in:
@@ -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
|
||||
);
|
||||
|
||||
34
app/db/models/calendar/recentWinners.sql
Normal file
34
app/db/models/calendar/recentWinners.sql
Normal 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";
|
||||
@@ -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
58
app/styles/front-page.css
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user