From 5efecd1706e3eaf0180394780aa47ee1140fe807 Mon Sep 17 00:00:00 2001 From: Kalle <38327916+Sendouc@users.noreply.github.com> Date: Sun, 17 Mar 2024 11:10:02 +0200 Subject: [PATCH] Badge show own at the top + search Closes #1631 --- app/features/badges/BadgeRepository.server.ts | 23 ++++- app/features/badges/routes/badges.tsx | 89 ++++++++++++++++--- app/styles/badges.css | 13 +++ locales/en/badges.json | 4 +- 4 files changed, 115 insertions(+), 14 deletions(-) diff --git a/app/features/badges/BadgeRepository.server.ts b/app/features/badges/BadgeRepository.server.ts index e9e8ce5cd..a9e02d03e 100644 --- a/app/features/badges/BadgeRepository.server.ts +++ b/app/features/badges/BadgeRepository.server.ts @@ -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({ diff --git a/app/features/badges/routes/badges.tsx b/app/features/badges/routes/badges.tsx index 2a113e16a..564352d27 100644 --- a/app/features/badges/routes/badges.tsx +++ b/app/features/badges/routes/badges.tsx @@ -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(); + 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 (
-
- {data.badges.map((badge) => ( - - - - ))} + } + value={inputValue} + onChange={(e) => setInputValue(e.target.value)} + /> + {ownBadges.length > 0 ? ( +
+ {t("badges:own.divider")} +
+ {ownBadges.map((badge) => ( + + + + ))} +
+
+ ) : null} +
+
+ {ownBadges.length > 0 ? ( + {t("badges:other.divider")} + ) : null} + {otherBadges.map((badge) => ( + + + + ))} +
@@ -66,3 +117,21 @@ export default function BadgesPageLayout() {
); } + +function splitBadges( + badges: BadgesLoaderData["badges"], + user: ReturnType, +) { + 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 }; +} diff --git a/app/styles/badges.css b/app/styles/badges.css index a926ee985..af612f42e 100644 --- a/app/styles/badges.css +++ b/app/styles/badges.css @@ -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; +} diff --git a/locales/en/badges.json b/locales/en/badges.json index 1d4243108..bb65d8f33 100644 --- a/locales/en/badges.json +++ b/locales/en/badges.json @@ -6,5 +6,7 @@ "tournament_other": "Awarded for winning {{tournament}} (×{{count}})", "forYourEvent": "Badge for your event?", "madeBy": "Badges by <2>borzoic", - "managedBy": "Managed by {{users}}" + "managedBy": "Managed by {{users}}", + "own.divider": "Managed badges", + "other.divider": "Other badges" }