- | {result.placement} |
+
+ {placementString(result.placement)}
+ |
{result.teamName} |
@@ -193,7 +195,7 @@ function Results() {
) : (
{
+ return [{ rel: "stylesheet", href: styles }];
+};
export const meta: MetaFunction = ({ data }: { data: UserPageLoaderData }) => {
return {
@@ -28,23 +31,9 @@ export const handle = {
export const userParamsSchema = z.object({ identifier: z.string() });
-export type UserPageLoaderData = Pick<
- User,
- | "id"
- | "discordName"
- | "discordAvatar"
- | "discordDiscriminator"
- | "discordId"
- | "youtubeId"
- | "twitch"
- | "twitter"
- | "bio"
-> & {
- country?: { emoji: string; code: string; name: string };
- badges: CountsByUserId;
-};
+export type UserPageLoaderData = UseDataFunctionReturn;
-export const loader: LoaderFunction = async ({ request, params }) => {
+export const loader = async ({ request, params }: LoaderArgs) => {
const locale = await i18next.getLocale(request);
const { identifier } = userParamsSchema.parse(params);
const user = notFoundIfFalsy(db.users.findByIdentifier(identifier));
@@ -53,7 +42,7 @@ export const loader: LoaderFunction = async ({ request, params }) => {
? countries[user.country as keyof typeof countries]
: undefined;
- return json({
+ return json({
id: user.id,
discordAvatar: user.discordAvatar,
discordDiscriminator: user.discordDiscriminator,
@@ -76,11 +65,12 @@ export const loader: LoaderFunction = async ({ request, params }) => {
}
: undefined,
badges: db.badges.countsByUserId(user.id),
+ results: db.calendarEvents.findResultsByUserId(user.id),
});
};
export default function UserPageLayout() {
- const data = useLoaderData();
+ const data = useLoaderData();
const user = useUser();
const { t } = useTranslation();
@@ -92,11 +82,16 @@ export default function UserPageLayout() {
{t("header.profile")}
- {isOwnPage ? (
+ {isOwnPage && (
{t("actions.edit")}
- ) : null}
+ )}
+ {data.results.length > 0 && (
+
+ {t("results")}
+
+ )}
diff --git a/app/routes/u.$identifier/index.tsx b/app/routes/u.$identifier/index.tsx
index b2f64ed63..e0baf46fa 100644
--- a/app/routes/u.$identifier/index.tsx
+++ b/app/routes/u.$identifier/index.tsx
@@ -1,23 +1,17 @@
-import type { LinksFunction } from "@remix-run/node";
import { useMatches } from "@remix-run/react";
+import clsx from "clsx";
+import * as React from "react";
+import { useTranslation } from "react-i18next";
import invariant from "tiny-invariant";
import { Avatar } from "~/components/Avatar";
-import styles from "~/styles/u.css";
-import type { UserPageLoaderData } from "../u.$identifier";
-import * as React from "react";
-import type { Unpacked } from "~/utils/types";
-import clsx from "clsx";
-import { assertUnreachable } from "~/utils/types";
+import { Badge } from "~/components/Badge";
import { TwitchIcon } from "~/components/icons/Twitch";
import { TwitterIcon } from "~/components/icons/Twitter";
import { YouTubeIcon } from "~/components/icons/YouTube";
+import type { Unpacked } from "~/utils/types";
+import { assertUnreachable } from "~/utils/types";
import { badgeExplanationText } from "../badges/$id";
-import { Badge } from "~/components/Badge";
-import { useTranslation } from "react-i18next";
-
-export const links: LinksFunction = () => {
- return [{ rel: "stylesheet", href: styles }];
-};
+import type { UserPageLoaderData } from "../u.$identifier";
export const handle = {
i18n: "badges",
diff --git a/app/routes/u.$identifier/results.tsx b/app/routes/u.$identifier/results.tsx
new file mode 100644
index 000000000..d9b5f9ea6
--- /dev/null
+++ b/app/routes/u.$identifier/results.tsx
@@ -0,0 +1,76 @@
+import { Link, useMatches } from "@remix-run/react";
+import { useTranslation } from "react-i18next";
+import invariant from "tiny-invariant";
+import { Avatar } from "~/components/Avatar";
+import { Section } from "~/components/Section";
+import { databaseTimestampToDate } from "~/utils/dates";
+import { discordFullName, placementString } from "~/utils/strings";
+import { userPage } from "~/utils/urls";
+import type { UserPageLoaderData } from "../u.$identifier";
+
+export default function UserResultsPage() {
+ const { i18n } = useTranslation();
+ const [, parentRoute] = useMatches();
+ invariant(parentRoute);
+ const data = parentRoute.data as UserPageLoaderData;
+
+ return (
+
+
+
+
+ | Placing |
+ Team |
+ Tournament |
+ Date |
+ Members |
+
+
+
+ {data.results.map((result) => (
+
+ |
+ {placementString(result.placement)}
+ |
+ {result.teamName} |
+ {result.eventName} |
+
+ {databaseTimestampToDate(result.startTime).toLocaleDateString(
+ i18n.language,
+ {
+ day: "numeric",
+ month: "numeric",
+ year: "numeric",
+ }
+ )}
+ |
+
+
+ {result.mates.map((player) => (
+ -
+ {typeof player === "string" ? (
+ player
+ ) : (
+
+ {" "}
+ {discordFullName(player)}
+
+ )}
+
+ ))}
+
+ |
+
+ ))}
+
+
+
+ );
+}
diff --git a/app/styles/calendar-event.css b/app/styles/calendar-event.css
index b18b67781..eb6d0c311 100644
--- a/app/styles/calendar-event.css
+++ b/app/styles/calendar-event.css
@@ -25,33 +25,10 @@
gap: var(--s-1);
}
-.event__results-table {
- width: 100%;
- border-collapse: separate;
- border-spacing: 0 var(--s-1-5);
- font-size: var(--fonts-sm);
- text-align: left;
-}
-
-.event__results-table > thead {
- font-size: var(--fonts-xxs);
-}
-
-.event__results-table > thead > tr {
- text-align: center;
-}
-
-.event__results-table > tbody > tr {
- padding-block: var(--s-2);
-}
-
-.event__results-table > tbody > tr:nth-child(2n) {
- background-color: var(--bg);
-}
-
.event__results-players {
display: flex;
flex-wrap: wrap;
+ padding: 0;
gap: var(--s-3);
list-style: none;
}
diff --git a/app/styles/common.css b/app/styles/common.css
index 332625d53..4559b8eba 100644
--- a/app/styles/common.css
+++ b/app/styles/common.css
@@ -263,6 +263,30 @@ select:focus {
outline: 2px solid var(--theme);
}
+table {
+ width: 100%;
+ border-collapse: separate;
+ border-spacing: 0 var(--s-1-5);
+ font-size: var(--fonts-xs);
+ text-align: left;
+}
+
+table > thead {
+ font-size: var(--fonts-xxs);
+}
+
+table > tbody > tr:nth-child(2n) {
+ background-color: var(--bg);
+}
+
+table > thead > tr > th {
+ padding-inline: var(--s-1);
+}
+
+table > tbody > tr > td {
+ padding-inline: var(--s-1);
+}
+
hr {
border-color: var(--theme-transparent);
}
diff --git a/app/styles/u.css b/app/styles/u.css
index 52b8732ae..713a71c62 100644
--- a/app/styles/u.css
+++ b/app/styles/u.css
@@ -129,3 +129,15 @@
font-size: var(--fonts-xxxs);
font-weight: var(--bold);
}
+
+.u__results-section {
+ overflow-x: auto;
+}
+
+.u__results-players {
+ display: flex;
+ flex-wrap: wrap;
+ padding: 0;
+ gap: var(--s-3);
+ list-style: none;
+}
diff --git a/app/utils/strings.ts b/app/utils/strings.ts
index eb3fe3bd1..01094b7ed 100644
--- a/app/utils/strings.ts
+++ b/app/utils/strings.ts
@@ -9,3 +9,11 @@ export function discordFullName(
export function makeTitle(title: string | string[]) {
return `${Array.isArray(title) ? title.join(" | ") : title} | sendou.ink`;
}
+
+export function placementString(placement: number) {
+ if (placement === 1) return "🥇";
+ if (placement === 2) return "🥈";
+ if (placement === 3) return "🥉";
+
+ return `${placement}th`;
+}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index d5fc1c09f..20953d587 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -18,5 +18,7 @@
"actions.save": "Save",
"actions.saving": "Saving...",
- "actions.edit": "Edit"
+ "actions.edit": "Edit",
+
+ "results": "Results"
}
|