From 5484ff49f02ed702f71bab06c9fffb9b01a7d980 Mon Sep 17 00:00:00 2001 From: Alisha Zaman Date: Sat, 31 Jan 2026 14:11:04 -0500 Subject: [PATCH 1/2] 648: Combined Leaderboard and LeaderboardEmbed --- .../embed/leaderboard/LeaderboardEmbed.tsx | 4 +- .../leaderboard/_components/OrgEmbedView.tsx | 246 ------------------ .../leaderboard/_components/Leaderboard.tsx | 181 ++++++++----- 3 files changed, 119 insertions(+), 312 deletions(-) delete mode 100644 js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx diff --git a/js/src/app/embed/leaderboard/LeaderboardEmbed.tsx b/js/src/app/embed/leaderboard/LeaderboardEmbed.tsx index 7c69bbb40..924bef8b4 100644 --- a/js/src/app/embed/leaderboard/LeaderboardEmbed.tsx +++ b/js/src/app/embed/leaderboard/LeaderboardEmbed.tsx @@ -1,4 +1,4 @@ -import OrgEmbedView from "@/app/embed/leaderboard/_components/OrgEmbedView"; +import { CurrentLeaderboard } from "@/app/leaderboard/_components/Leaderboard"; import { Box } from "@mantine/core"; export default function LeaderboardEmbed() { @@ -6,7 +6,7 @@ export default function LeaderboardEmbed() { <> - + diff --git a/js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx b/js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx deleted file mode 100644 index 9b88e3963..000000000 --- a/js/src/app/embed/leaderboard/_components/OrgEmbedView.tsx +++ /dev/null @@ -1,246 +0,0 @@ -import OrgHeader from "@/app/embed/leaderboard/_components/OrgHeader"; -import LeaderboardSkeleton from "@/app/leaderboard/_components/LeaderboardSkeleton"; -import LeaderboardCard from "@/components/ui/LeaderboardCard"; -import CustomPagination from "@/components/ui/table/CustomPagination"; -import SearchBox from "@/components/ui/table/SearchBox"; -import Toast from "@/components/ui/toast/Toast"; -import { useCurrentLeaderboardUsersQuery } from "@/lib/api/queries/leaderboard"; -import getOrdinal from "@/lib/helper/ordinal"; -import { theme } from "@/lib/theme"; -import { - Box, - Button, - Center, - Flex, - Overlay, - Table, - Tooltip, -} from "@mantine/core"; -import { IconCircleCheckFilled } from "@tabler/icons-react"; -import { useEffect, useMemo } from "react"; -import { FaArrowLeft, FaArrowRight, FaDiscord } from "react-icons/fa"; -import { SiLeetcode } from "react-icons/si"; -import { Link, useSearchParams } from "react-router-dom"; - -export default function OrgLeaderboardEmbed() { - const [searchParams] = useSearchParams(); - - const pageSizeParam = searchParams.get("pageSize"); - const pageSize = pageSizeParam ? Number(pageSizeParam) : undefined; - - const { - status, - isPlaceholderData, - data, - page, - goTo, - goBack, - goForward, - setSearchQuery, - searchQuery, - debouncedQuery, - filters, - onFilterReset, - } = useCurrentLeaderboardUsersQuery({ pageSize }); - - const activeFilter = useMemo(() => { - const active = Object.typedEntries(filters).filter( - ([, enabled]) => enabled, - ); - - return active.length === 1 ? active[0][0] : undefined; - }, [filters]); - - useEffect(() => { - const activeCount = Object.typedEntries(filters).filter( - ([, enabled]) => enabled, - ).length; - - if (activeCount > 1) { - onFilterReset(); - } - }, [filters, onFilterReset]); - - if (status === "pending") { - return ; - } - - if (status === "error") { - return ; - } - - if (!data.success) { - return

Sorry, there are no users to display.

; - } - - const pageData = data.payload; - const [first, second, third] = pageData.items; - - return ( - <> - -
- -
- - {page === 1 && second && !debouncedQuery && ( - - - - )} - {page === 1 && first && !debouncedQuery && ( - - )} - {page === 1 && third && !debouncedQuery && ( - - )} - - { - setSearchQuery(event.currentTarget.value); - }} - placeholder={"Search for User"} - smallPadding - /> - - - {isPlaceholderData && ( - - )} - - - # - Name - Pts - - - - {pageData.items.map(({ index: rank, ...entry }, index) => { - if (page === 1 && !debouncedQuery && [0, 1, 2].includes(index)) - return null; - return ( - - {rank} - - - {entry.nickname ? - - - {" "} - {entry.nickname} - - - : - {" "} - {entry.discordName} - - } - - {" "} - {entry.leetcodeUsername} - - - - {entry.totalScore} - - ); - })} - -
-
-
- - - - - -
- - ); -} diff --git a/js/src/app/leaderboard/_components/Leaderboard.tsx b/js/src/app/leaderboard/_components/Leaderboard.tsx index dc07339aa..cde168699 100644 --- a/js/src/app/leaderboard/_components/Leaderboard.tsx +++ b/js/src/app/leaderboard/_components/Leaderboard.tsx @@ -1,3 +1,4 @@ +import OrgHeader from "@/app/embed/leaderboard/_components/OrgHeader"; import LeaderboardSkeleton from "@/app/leaderboard/_components/LeaderboardSkeleton"; import FilterDropdown from "@/components/ui/dropdown/FilterDropdown"; import FilterDropdownItem from "@/components/ui/dropdown/FilterDropdownItem"; @@ -34,12 +35,25 @@ import { Tooltip, } from "@mantine/core"; import { IconCircleCheckFilled } from "@tabler/icons-react"; +import { useEffect, useMemo } from "react"; import { FaArrowLeft, FaArrowRight, FaDiscord } from "react-icons/fa"; import { SiLeetcode } from "react-icons/si"; import { Link } from "react-router-dom"; -export function CurrentLeaderboard() { - const query = useCurrentLeaderboardUsersQuery(); +type LeaderboardOptions = { + embedded?: boolean; +}; + +function getPageSizeFromParams(): number | undefined { + const searchParams = new URLSearchParams(window.location.search); + const pageSizeParam = searchParams.get("pageSize"); + return pageSizeParam ? Number(pageSizeParam) : undefined; +} + +export function CurrentLeaderboard(props: LeaderboardOptions = {}) { + const query = useCurrentLeaderboardUsersQuery( + props.embedded ? { pageSize: getPageSizeFromParams() } : {}, + ); const metadataQuery = useCurrentLeaderboardMetadataQuery(); const dateRange = @@ -52,11 +66,15 @@ export function CurrentLeaderboard() { query={query} startDate={dateRange?.startDate} endDate={dateRange?.endDate} + {...props} /> ); } -export function LeaderboardById({ leaderboardId }: { leaderboardId: string }) { +export function LeaderboardById({ + leaderboardId, + ...props +}: LeaderboardOptions & { leaderboardId: string }) { const query = useLeaderboardUsersByIdQuery({ leaderboardId }); const metadataQuery = useLeaderboardMetadataByIdQuery(leaderboardId); @@ -70,6 +88,7 @@ export function LeaderboardById({ leaderboardId }: { leaderboardId: string }) { query={query} startDate={dateRange?.startDate} endDate={dateRange?.endDate} + {...props} /> ); } @@ -78,7 +97,8 @@ function LeaderboardIndex({ query, startDate, endDate, -}: { + embedded = false, +}: LeaderboardOptions & { query: ReturnType; startDate?: string; endDate?: string; @@ -102,6 +122,24 @@ function LeaderboardIndex({ onFilterReset, } = query; + const activeFilter = useMemo(() => { + const active = Object.typedEntries(filters).filter( + ([, enabled]) => enabled, + ); + return active.length === 1 ? active[0][0] : undefined; + }, [filters]); + + useEffect(() => { + if (embedded) { + const activeCount = Object.typedEntries(filters).filter( + ([, enabled]) => enabled, + ).length; + if (activeCount > 1) { + onFilterReset(); + } + } + }, [embedded, filters, onFilterReset]); + if (status === "pending") { return ; } @@ -116,8 +154,9 @@ function LeaderboardIndex({ const pageData = data.payload; const [first, second, third] = pageData.items; + const shouldShowTopThree = page === 1 && !debouncedQuery; const cardItems = pageData.items.filter((_, index) => { - if (page === 1 && !debouncedQuery && [0, 1, 2].includes(index)) { + if (shouldShowTopThree && [0, 1, 2].includes(index)) { return false; } return true; @@ -127,6 +166,19 @@ function LeaderboardIndex({ return ( <> + {embedded && } + {embedded && ( +
+ +
+ )} - {page === 1 && second && !debouncedQuery && ( + {shouldShowTopThree && second && ( )} - {page === 1 && first && !debouncedQuery && ( + {shouldShowTopThree && first && ( )} - {page === 1 && third && !debouncedQuery && ( + {shouldShowTopThree && third && ( )} - - - {schoolFF && - ApiUtils.getAllSupportedTagEnums().map((tagEnum) => ( - { - const metadata = ApiUtils.getMetadataByTagEnum(tagEnum); - - return ( - - {metadata.shortName} - {metadata.alt} - - ); - }} - value={filters[tagEnum]} - toggle={() => toggleFilter(tagEnum)} - /> - ))} - - Toggle Global Rank - - } - /> - - - + {schoolFF && + ApiUtils.getAllSupportedTagEnums().map((tagEnum) => ( + { + const metadata = ApiUtils.getMetadataByTagEnum(tagEnum); + return ( + + {metadata.shortName} + {metadata.alt} + + ); + }} + value={filters[tagEnum]} + toggle={() => toggleFilter(tagEnum)} + /> + ))} + + Toggle Global Rank + + } + /> + + + + )} { From 50c96f73220a0ae93a884196eadab25f3f6c23d4 Mon Sep 17 00:00:00 2001 From: Alisha Zaman Date: Sat, 31 Jan 2026 14:19:02 -0500 Subject: [PATCH 2/2] 648: Added Co-Pilot suggestion --- js/src/app/leaderboard/_components/Leaderboard.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/js/src/app/leaderboard/_components/Leaderboard.tsx b/js/src/app/leaderboard/_components/Leaderboard.tsx index cde168699..6205a2c6b 100644 --- a/js/src/app/leaderboard/_components/Leaderboard.tsx +++ b/js/src/app/leaderboard/_components/Leaderboard.tsx @@ -123,9 +123,8 @@ function LeaderboardIndex({ } = query; const activeFilter = useMemo(() => { - const active = Object.typedEntries(filters).filter( - ([, enabled]) => enabled, - ); + const entries = filters ? Object.typedEntries(filters) : []; + const active = entries.filter(([, enabled]) => enabled); return active.length === 1 ? active[0][0] : undefined; }, [filters]);