mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-10-01 07:37:55 -05:00
Badge show own at the top + search Closes #1631
This commit is contained in:
@@ -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({
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user