From b3edd572f961538f9f16a45310fe69c7663b8b77 Mon Sep 17 00:00:00 2001 From: ibrahimmujidah1-stack <296183415+ibrahimmujidah1-stack@users.noreply.github.com> Date: Tue, 20 Oct 2026 22:59:23 +0100 Subject: [PATCH] feat(discovery): implement key discovery leaderboard page (#1206) - Add LeaderboardMetric, KeyLeaderboardEntry types and normalizer - Add getKeyDiscoveryLeaderboard to analyticsService with fallback mock data - Add keyLeaderboardService and useKeyDiscoveryLeaderboard React Query hook - Create KeyDiscoveryLeaderboardPage with Volume, Holders, Reputation Score metric tabs - Add real-time search filtering by key name and creator - Add rank badges, metric value formatting, and 24h change indicators with positive/negative styling - Add connected wallet creator key highlighting - Register /discovery/leaderboard route and link from DiscoveryPage - Add comprehensive integration and service tests --- src/hooks/useKeyDiscoveryLeaderboard.ts | 29 + src/lib/queryKeys.ts | 10 +- src/pages/DiscoveryPage.tsx | 11 +- src/pages/KeyDiscoveryLeaderboardPage.tsx | 518 ++++++++++++++++++ .../KeyDiscoveryLeaderboardPage.test.tsx | 241 ++++++++ src/routes.tsx | 9 + .../__tests__/keyLeaderboard.service.test.ts | 125 +++++ src/services/analytics.service.ts | 248 +++++++++ src/services/keyLeaderboard.service.ts | 24 + 9 files changed, 1213 insertions(+), 2 deletions(-) create mode 100644 src/hooks/useKeyDiscoveryLeaderboard.ts create mode 100644 src/pages/KeyDiscoveryLeaderboardPage.tsx create mode 100644 src/pages/__tests__/KeyDiscoveryLeaderboardPage.test.tsx create mode 100644 src/services/__tests__/keyLeaderboard.service.test.ts create mode 100644 src/services/keyLeaderboard.service.ts diff --git a/src/hooks/useKeyDiscoveryLeaderboard.ts b/src/hooks/useKeyDiscoveryLeaderboard.ts new file mode 100644 index 00000000..33a98e8c --- /dev/null +++ b/src/hooks/useKeyDiscoveryLeaderboard.ts @@ -0,0 +1,29 @@ +import { useQuery } from '@tanstack/react-query'; +import { queryKeys } from '@/lib/queryKeys'; +import { + analyticsService, + type LeaderboardMetric, + type KeyLeaderboardEntry, +} from '@/services/analytics.service'; + +export interface UseKeyDiscoveryLeaderboardOptions { + enabled?: boolean; + refetchInterval?: number | false; +} + +/** + * Hook to fetch top performing creator keys discovery leaderboard (#1206). + * Ranked by volume, holders, or reputation score. + */ +export function useKeyDiscoveryLeaderboard( + metric: LeaderboardMetric = 'volume', + options: UseKeyDiscoveryLeaderboardOptions = {} +) { + return useQuery({ + queryKey: queryKeys.keyLeaderboard.byMetric(metric), + queryFn: () => analyticsService.getKeyDiscoveryLeaderboard(metric), + enabled: options.enabled ?? true, + refetchInterval: options.refetchInterval ?? false, + staleTime: 30_000, + }); +} diff --git a/src/lib/queryKeys.ts b/src/lib/queryKeys.ts index 35e74288..3473c9d0 100644 --- a/src/lib/queryKeys.ts +++ b/src/lib/queryKeys.ts @@ -9,7 +9,10 @@ import type { GetCoursesParams } from '@/services/course.service'; import type { VolumeWindow } from '@/services/leaderboard.service'; -import type { ProtocolTimeRange } from '@/services/analytics.service'; +import type { + ProtocolTimeRange, + LeaderboardMetric, +} from '@/services/analytics.service'; export const queryKeys = { creatorProfile: { @@ -224,6 +227,11 @@ export const queryKeys = { data: (range: ProtocolTimeRange = '30d') => ['protocolAnalytics', 'data', range] as const, }, + keyLeaderboard: { + all: () => ['analytics', 'keys', 'leaderboard'] as const, + byMetric: (metric: LeaderboardMetric = 'volume') => + ['analytics', 'keys', 'leaderboard', metric] as const, + }, creatorRevenue: { all: () => ['creatorRevenue'] as const, summary: (creatorId: string) => diff --git a/src/pages/DiscoveryPage.tsx b/src/pages/DiscoveryPage.tsx index b1c5219a..8ebc1a80 100644 --- a/src/pages/DiscoveryPage.tsx +++ b/src/pages/DiscoveryPage.tsx @@ -1,5 +1,5 @@ import { Link } from 'react-router'; -import { ArrowRight, RefreshCw, Flame, Sparkles, Layers } from 'lucide-react'; +import { ArrowRight, RefreshCw, Flame, Sparkles, Layers, Trophy } from 'lucide-react'; import { useDocumentTitle } from '@/hooks/useDocumentTitle'; import { useNavigationTiming } from '@/hooks/useNavigationTiming'; import { useKeyDiscovery } from '@/hooks/useKeyDiscovery'; @@ -110,6 +110,15 @@ export default function DiscoveryPage() { )} + + +