Badge show own at the top + search Closes #1631

This commit is contained in:
Kalle
2024-03-17 11:10:02 +02:00
parent b22e013e82
commit 5efecd1706
4 changed files with 115 additions and 14 deletions

View File

@@ -1,12 +1,29 @@
import { jsonArrayFrom } from "kysely/helpers/sqlite";
import { db } from "~/db/sql";
import { COMMON_USER_FIELDS } from "~/utils/kysely.server";
import type { Unwrapped } from "~/utils/types";
export function all() {
return db
export async function all() {
const rows = await db
.selectFrom("Badge")
.select(["id", "displayName", "code", "hue"])
.select(({ eb }) => [
"id",
"displayName",
"code",
"hue",
jsonArrayFrom(
eb
.selectFrom("BadgeManager")
.whereRef("BadgeManager.badgeId", "=", "Badge.id")
.select(["userId"]),
).as("managers"),
])
.execute();
return rows.map((row) => ({
...row,
managers: row.managers.map((m) => m.userId),
}));
}
export async function findByOwnerId({

View File

@@ -12,6 +12,11 @@ import { Trans } from "react-i18next";
import { useTranslation } from "react-i18next";
import { type SendouRouteHandle } from "~/utils/remix";
import * as BadgeRepository from "../BadgeRepository.server";
import { useUser } from "~/features/auth/core/user";
import { Divider } from "~/components/Divider";
import { Input } from "~/components/Input";
import { SearchIcon } from "~/components/icons/Search";
import * as React from "react";
import "~/styles/badges.css";
@@ -33,21 +38,67 @@ export const loader = async () => {
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 />
<div className="badges__small-badges">
{data.badges.map((badge) => (
<NavLink
className="badges__nav-link"
key={badge.id}
to={String(badge.id)}
>
<Badge badge={badge} size={64} isAnimated={false} />
</NavLink>
))}
<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">
@@ -66,3 +117,21 @@ export default function BadgesPageLayout() {
</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 };
}

View File

@@ -15,6 +15,7 @@
flex-wrap: wrap;
justify-content: center;
gap: var(--s-2);
margin-block-start: var(--s-1);
}
.badges__nav-link.active {
@@ -116,3 +117,15 @@
.badges-edit__submit-button {
margin: 0 auto;
}
.badges-search__input {
height: 40px !important;
margin: 0 auto;
font-size: var(--fonts-lg);
}
.badges-search__icon {
height: 25px;
margin: auto;
margin-right: 15px;
}

View File

@@ -6,5 +6,7 @@
"tournament_other": "Awarded for winning {{tournament}} (×{{count}})",
"forYourEvent": "Badge for your event?",
"madeBy": "Badges by <2>borzoic</2>",
"managedBy": "Managed by {{users}}"
"managedBy": "Managed by {{users}}",
"own.divider": "Managed badges",
"other.divider": "Other badges"
}