mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
124 lines
3.3 KiB
TypeScript
124 lines
3.3 KiB
TypeScript
import type { SerializeFrom } from "@remix-run/node";
|
|
import { Link, NavLink, Outlet, useLoaderData } from "@remix-run/react";
|
|
import * as React from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { Badge } from "~/components/Badge";
|
|
import { Divider } from "~/components/Divider";
|
|
import { Input } from "~/components/Input";
|
|
import { Main } from "~/components/Main";
|
|
import { SearchIcon } from "~/components/icons/Search";
|
|
import { useUser } from "~/features/auth/core/user";
|
|
import type { SendouRouteHandle } from "~/utils/remix";
|
|
import { BADGES_PAGE, FAQ_PAGE, navIconUrl } from "~/utils/urls";
|
|
import * as BadgeRepository from "../BadgeRepository.server";
|
|
|
|
import "~/styles/badges.css";
|
|
|
|
export const handle: SendouRouteHandle = {
|
|
i18n: "badges",
|
|
breadcrumb: () => ({
|
|
imgPath: navIconUrl("badges"),
|
|
href: BADGES_PAGE,
|
|
type: "IMAGE",
|
|
}),
|
|
};
|
|
|
|
export type BadgesLoaderData = SerializeFrom<typeof loader>;
|
|
|
|
export const loader = async () => {
|
|
return { badges: await BadgeRepository.all() };
|
|
};
|
|
|
|
export default function BadgesPageLayout() {
|
|
const { t } = useTranslation(["badges"]);
|
|
const data = useLoaderData<typeof loader>();
|
|
const user = useUser();
|
|
const [inputValue, setInputValue] = React.useState("");
|
|
|
|
const { ownBadges: allOwnBadges, otherBadges: allOtherBadges } = splitBadges(
|
|
data.badges,
|
|
user,
|
|
);
|
|
|
|
const inputValueNormalized = inputValue.toLowerCase();
|
|
const ownBadges = allOwnBadges.filter(
|
|
(b) =>
|
|
!inputValueNormalized ||
|
|
b.displayName.toLowerCase().includes(inputValueNormalized),
|
|
);
|
|
const otherBadges = allOtherBadges.filter(
|
|
(b) =>
|
|
!inputValueNormalized ||
|
|
b.displayName.toLowerCase().includes(inputValueNormalized),
|
|
);
|
|
|
|
return (
|
|
<Main>
|
|
<div className="badges__container">
|
|
<Outlet />
|
|
<Input
|
|
className="badges-search__input"
|
|
icon={<SearchIcon className="badges-search__icon" />}
|
|
value={inputValue}
|
|
onChange={(e) => setInputValue(e.target.value)}
|
|
/>
|
|
{ownBadges.length > 0 ? (
|
|
<div className="w-full">
|
|
<Divider smallText>{t("badges:own.divider")}</Divider>
|
|
<div className="badges__small-badges">
|
|
{ownBadges.map((badge) => (
|
|
<NavLink
|
|
className="badges__nav-link"
|
|
key={badge.id}
|
|
to={String(badge.id)}
|
|
>
|
|
<Badge badge={badge} size={64} isAnimated={false} />
|
|
</NavLink>
|
|
))}
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
<div className="w-full">
|
|
<div className="badges__small-badges">
|
|
{ownBadges.length > 0 ? (
|
|
<Divider smallText>{t("badges:other.divider")}</Divider>
|
|
) : null}
|
|
{otherBadges.map((badge) => (
|
|
<NavLink
|
|
className="badges__nav-link"
|
|
key={badge.id}
|
|
to={String(badge.id)}
|
|
>
|
|
<Badge badge={badge} size={64} isAnimated={false} />
|
|
</NavLink>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="badges__general-info-texts">
|
|
<p>
|
|
<Link to={FAQ_PAGE}>{t("forYourEvent")}</Link>
|
|
</p>
|
|
</div>
|
|
</Main>
|
|
);
|
|
}
|
|
|
|
function splitBadges(
|
|
badges: BadgesLoaderData["badges"],
|
|
user: ReturnType<typeof useUser>,
|
|
) {
|
|
const ownBadges: BadgesLoaderData["badges"] = [];
|
|
const otherBadges: BadgesLoaderData["badges"] = [];
|
|
|
|
for (const badge of badges) {
|
|
if (user && badge.managers.includes(user?.id)) {
|
|
ownBadges.push(badge);
|
|
} else {
|
|
otherBadges.push(badge);
|
|
}
|
|
}
|
|
|
|
return { ownBadges, otherBadges };
|
|
}
|