Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/leaderboard/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { LeaderboardTable } from "@/components/leaderboard/leaderboard-table";
import { LeaderboardFilters } from "@/components/leaderboard/leaderboard-filters";
import { UserRankSidebar } from "@/components/leaderboard/user-rank-sidebar";
import { LeaderboardFilters as FiltersType, ReputationTier } from "@/types/leaderboard";
import { LeaderboardTimeframe } from "@/lib/graphql/generated";
import { useState, useEffect } from "react";
import { useRouter, useSearchParams } from "next/navigation";
import { TIMEFRAMES, TIERS } from "@/components/leaderboard/leaderboard-filters";
Expand All @@ -22,7 +23,7 @@ export default function LeaderboardPage() {

const initialTimeframe = TIMEFRAMES.some(t => t.value === rawTimeframe)
? (rawTimeframe as FiltersType["timeframe"])
: "ALL_TIME";
: LeaderboardTimeframe.AllTime;

const initialTier = TIERS.some(t => t.value === rawTier)
? (rawTier as ReputationTier)
Expand Down Expand Up @@ -65,7 +66,7 @@ export default function LeaderboardPage() {
// Sync debounced filters to URL
useEffect(() => {
const params = new URLSearchParams();
if (debouncedFilters.timeframe !== "ALL_TIME") params.set("timeframe", debouncedFilters.timeframe);
if (debouncedFilters.timeframe !== LeaderboardTimeframe.AllTime) params.set("timeframe", debouncedFilters.timeframe);
if (debouncedFilters.tier) params.set("tier", debouncedFilters.tier);
if (debouncedFilters.tags && debouncedFilters.tags.length > 0) {
params.set("tags", debouncedFilters.tags.join(","));
Expand Down
25 changes: 12 additions & 13 deletions components/leaderboard/leaderboard-filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,9 @@ import {
SelectValue,
} from "@/components/ui/select";
import {
LeaderboardFilters as FiltersType,
LeaderboardTimeframe,
ReputationTier
LeaderboardFilters as FiltersType
} from "@/types/leaderboard";
import { LeaderboardTimeframe, ReputationTier } from "@/lib/graphql/generated";
import { FilterX } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import {
Expand All @@ -38,17 +37,17 @@ interface LeaderboardFiltersProps {
}

export const TIMEFRAMES: { value: LeaderboardTimeframe; label: string }[] = [
{ value: "ALL_TIME", label: "All Time" },
{ value: "THIS_MONTH", label: "This Month" },
{ value: "THIS_WEEK", label: "This Week" },
{ value: LeaderboardTimeframe.AllTime, label: "All Time" },
{ value: LeaderboardTimeframe.ThisMonth, label: "This Month" },
{ value: LeaderboardTimeframe.ThisWeek, label: "This Week" },
];

export const TIERS: { value: ReputationTier; label: string }[] = [
{ value: "LEGEND", label: "Legend" },
{ value: "EXPERT", label: "Expert" },
{ value: "ESTABLISHED", label: "Established" },
{ value: "CONTRIBUTOR", label: "Contributor" },
{ value: "NEWCOMER", label: "Newcomer" },
{ value: ReputationTier.Legend, label: "Legend" },
{ value: ReputationTier.Expert, label: "Expert" },
{ value: ReputationTier.Established, label: "Established" },
{ value: ReputationTier.Contributor, label: "Contributor" },
{ value: ReputationTier.Newcomer, label: "Newcomer" },
];

// Mock available tags for filter - in real app could be passed as prop
Expand All @@ -72,13 +71,13 @@ export function LeaderboardFilters({ filters, onFilterChange }: LeaderboardFilte

const clearFilters = () => {
onFilterChange({
timeframe: "ALL_TIME",
timeframe: LeaderboardTimeframe.AllTime,
tier: undefined,
tags: [],
});
};

const hasActiveFilters = filters.timeframe !== "ALL_TIME" || filters.tier || (filters.tags?.length || 0) > 0;
const hasActiveFilters = filters.timeframe !== LeaderboardTimeframe.AllTime || filters.tier || (filters.tags?.length || 0) > 0;

return (
<div className="flex flex-wrap items-center gap-3 text-foreground">
Expand Down
3 changes: 2 additions & 1 deletion components/leaderboard/mini-leaderboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { TierBadge } from "@/components/reputation/tier-badge";
import { Trophy, ChevronRight, AlertCircle } from "lucide-react";
import Link from "next/link";
import { cn } from "@/lib/utils";
import { LeaderboardContributor } from "@/lib/graphql/generated";

interface MiniLeaderboardProps {
className?: string;
Expand Down Expand Up @@ -61,7 +62,7 @@ export function MiniLeaderboard({
</div>
) : (
<div className="flex flex-col">
{contributors?.map((contributor, index) => (
{contributors?.map((contributor: LeaderboardContributor, index: number) => (
<Link
key={contributor.id}
href={`/profile/${contributor.userId}`}
Expand Down
48 changes: 38 additions & 10 deletions hooks/use-leaderboard.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
import { useQuery, useInfiniteQuery, useQueryClient } from '@tanstack/react-query';
import { leaderboardApi } from '@/lib/api/leaderboard';
import { LeaderboardFilters } from '@/types/leaderboard';
import { fetcher } from '@/lib/graphql/client';
import {
LeaderboardQuery,
LeaderboardQueryVariables,
UserLeaderboardRankQuery,
UserLeaderboardRankQueryVariables,
TopContributorsQuery,
TopContributorsQueryVariables
} from '@/lib/graphql/generated';
import { LeaderboardFilters, LeaderboardResponse } from '@/types/leaderboard';

export const LEADERBOARD_KEYS = {
all: ['leaderboard'] as const,
Expand All @@ -13,9 +21,13 @@
export const useLeaderboard = (filters: LeaderboardFilters, limit: number = 20) => {
return useInfiniteQuery({
queryKey: LEADERBOARD_KEYS.list(filters),
queryFn: ({ pageParam = 1 }) =>
leaderboardApi.fetchLeaderboard(filters, { page: pageParam, limit }),
getNextPageParam: (lastPage, allPages) => {
queryFn: ({ pageParam = 1 }) => {
return fetcher<LeaderboardQuery, LeaderboardQueryVariables>(
require('@/lib/graphql/generated').LeaderboardDocument,

Check failure on line 26 in hooks/use-leaderboard.ts

View workflow job for this annotation

GitHub Actions / build-and-lint (24.x)

A `require()` style import is forbidden
{ filters, pagination: { page: pageParam as number, limit } }
)().then(data => data.leaderboard);
},
getNextPageParam: (lastPage: LeaderboardResponse, allPages: LeaderboardResponse[]) => {
// Optimization: Use simple math instead of iterating all entries
if (allPages.length * limit < lastPage.totalCount) {
return allPages.length + 1;
Expand All @@ -30,15 +42,26 @@
export const useUserRank = (userId?: string) => {
return useQuery({
queryKey: LEADERBOARD_KEYS.user(userId || ''),
queryFn: () => leaderboardApi.fetchUserRank(userId),
queryFn: () => {
if (!userId) return null;
return fetcher<UserLeaderboardRankQuery, UserLeaderboardRankQueryVariables>(
require('@/lib/graphql/generated').UserLeaderboardRankDocument,

Check failure on line 48 in hooks/use-leaderboard.ts

View workflow job for this annotation

GitHub Actions / build-and-lint (24.x)

A `require()` style import is forbidden
{ userId }
)().then(data => data.userLeaderboardRank);
},
enabled: !!userId,
});
};

export const useTopContributors = (count: number = 5) => {
return useQuery({
queryKey: LEADERBOARD_KEYS.top(count),
queryFn: () => leaderboardApi.fetchTopContributors(count),
queryFn: () => {
return fetcher<TopContributorsQuery, TopContributorsQueryVariables>(
require('@/lib/graphql/generated').TopContributorsDocument,

Check failure on line 61 in hooks/use-leaderboard.ts

View workflow job for this annotation

GitHub Actions / build-and-lint (24.x)

A `require()` style import is forbidden
{ count }
)().then(data => data.topContributors);
},
staleTime: 1000 * 60 * 15, // 15 minutes
});
};
Expand All @@ -49,10 +72,15 @@
return (filters: LeaderboardFilters, page: number, limit: number) => {
queryClient.prefetchInfiniteQuery({
queryKey: LEADERBOARD_KEYS.list(filters),
queryFn: ({ pageParam }) => leaderboardApi.fetchLeaderboard(filters, { page: pageParam as number, limit }),
queryFn: ({ pageParam }: { pageParam?: number }) => {
return fetcher<LeaderboardQuery, LeaderboardQueryVariables>(
require('@/lib/graphql/generated').LeaderboardDocument,

Check failure on line 77 in hooks/use-leaderboard.ts

View workflow job for this annotation

GitHub Actions / build-and-lint (24.x)

A `require()` style import is forbidden
{ filters, pagination: { page: pageParam as number, limit } }
)().then(data => data.leaderboard);
},
initialPageParam: 1,
getNextPageParam: (lastPage, allPages) => {
const loadedCount = allPages.flatMap(p => p.entries).length;
getNextPageParam: (lastPage: LeaderboardResponse, allPages: LeaderboardResponse[]) => {
const loadedCount = allPages.flatMap((p: LeaderboardResponse) => p.entries).length;
if (loadedCount < lastPage.totalCount) {
return allPages.length + 1;
}
Expand Down
35 changes: 0 additions & 35 deletions lib/api/leaderboard.ts

This file was deleted.

Loading
Loading