-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from Ashutoshbind15/main
Add the live leaderboard
- Loading branch information
Showing
10 changed files
with
437 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
"use client"; | ||
|
||
import { api } from "@/convex/_generated/api"; | ||
import { useQuery } from "convex/react"; | ||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; | ||
import { | ||
Table, | ||
TableBody, | ||
TableCaption, | ||
TableCell, | ||
TableHead, | ||
TableHeader, | ||
TableRow, | ||
} from "@/components/ui/table"; | ||
|
||
// Define the types for the data | ||
interface Ranking { | ||
_id: string; | ||
modelId: string; | ||
level?: string; | ||
wins: number; | ||
losses: number; | ||
} | ||
|
||
interface Stats { | ||
wins: number; | ||
losses: number; | ||
total: number; | ||
ratio: number; | ||
} | ||
|
||
const LeaderBoard = () => { | ||
const globalRanking = useQuery(api.leaderboard.getGlobalRankings) as | ||
| Ranking[] | ||
| undefined; | ||
const levelRanking = useQuery(api.leaderboard.getLevelRankings) as | ||
| Ranking[] | ||
| undefined; | ||
|
||
// Transform the levelRanking data into a pivot table structure | ||
const pivotLevelData = (levelRanking: Ranking[] | undefined) => { | ||
const levels: Record<string, Record<string, Stats>> = {}; | ||
|
||
levelRanking?.forEach((item) => { | ||
if (!levels[item.level!]) { | ||
levels[item.level!] = {}; | ||
} | ||
|
||
levels[item.level!][item.modelId] = { | ||
wins: item.wins, | ||
losses: item.losses, | ||
total: item.wins + item.losses, | ||
ratio: item.wins / (item.wins + item.losses), | ||
}; | ||
}); | ||
|
||
return levels; | ||
}; | ||
|
||
const pivotedLevelData = pivotLevelData(levelRanking); | ||
|
||
// Get all unique model IDs to dynamically create columns | ||
const allModels = Array.from( | ||
new Set(levelRanking?.map((item) => item.modelId)), | ||
); | ||
|
||
return ( | ||
<div className="py-12"> | ||
<div className="text-center text-3xl font-semibold mb-6">Leaderboard</div> | ||
|
||
<Tabs defaultValue="global" className=""> | ||
<TabsList> | ||
<TabsTrigger value="global">Global Rankings</TabsTrigger> | ||
<TabsTrigger value="level">Map based Rankings</TabsTrigger> | ||
</TabsList> | ||
<TabsContent value="global" className="p-4"> | ||
{/* Global Rankings Table */} | ||
<Table> | ||
<TableCaption>The global model realtime tally.</TableCaption> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead className="w-[200px]">Model ID</TableHead> | ||
<TableHead>Number of Wins</TableHead> | ||
<TableHead>Number of Losses</TableHead> | ||
<TableHead>Total Games</TableHead> | ||
<TableHead className="text-right">Ratio</TableHead> | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{globalRanking?.map((item) => ( | ||
<TableRow key={item._id}> | ||
<TableCell>{item.modelId}</TableCell> | ||
<TableCell>{item.wins}</TableCell> | ||
<TableCell>{item.losses}</TableCell> | ||
<TableCell>{item.wins + item.losses}</TableCell> | ||
<TableCell className="text-right"> | ||
{(item.wins / (item.wins + item.losses)).toFixed(2)} | ||
</TableCell> | ||
</TableRow> | ||
))} | ||
</TableBody> | ||
</Table> | ||
</TabsContent> | ||
<TabsContent value="level" className="p-4"> | ||
{/* Map-based Rankings Pivoted Table */} | ||
<Table> | ||
<TableCaption>The models map-based tally per level.</TableCaption> | ||
<TableHeader> | ||
<TableRow> | ||
<TableHead>Level</TableHead> | ||
{/* Dynamically render column headers for each model */} | ||
{allModels.map((modelId) => ( | ||
<TableHead key={modelId}>{modelId}</TableHead> | ||
))} | ||
</TableRow> | ||
</TableHeader> | ||
<TableBody> | ||
{/* Render rows for each level */} | ||
{Object.entries(pivotedLevelData).map(([level, models]) => ( | ||
<TableRow key={level}> | ||
<TableCell>{level}</TableCell> | ||
{/* Render stats for each model in the columns */} | ||
{allModels.map((modelId) => { | ||
const stats = models[modelId] || { | ||
wins: 0, | ||
losses: 0, | ||
total: 0, | ||
ratio: 0, | ||
}; | ||
return ( | ||
<TableCell key={modelId}> | ||
Wins: {stats.wins}, Losses: {stats.losses}, Games:{" "} | ||
{stats.total}, Ratio: {stats.ratio.toFixed(2)} | ||
</TableCell> | ||
); | ||
})} | ||
</TableRow> | ||
))} | ||
</TableBody> | ||
</Table> | ||
</TabsContent> | ||
</Tabs> | ||
</div> | ||
); | ||
}; | ||
|
||
export default LeaderBoard; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import * as React from "react" | ||
|
||
import { cn } from "@/lib/utils" | ||
|
||
const Table = React.forwardRef< | ||
HTMLTableElement, | ||
React.HTMLAttributes<HTMLTableElement> | ||
>(({ className, ...props }, ref) => ( | ||
<div className="relative w-full overflow-auto"> | ||
<table | ||
ref={ref} | ||
className={cn("w-full caption-bottom text-sm", className)} | ||
{...props} | ||
/> | ||
</div> | ||
)) | ||
Table.displayName = "Table" | ||
|
||
const TableHeader = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} /> | ||
)) | ||
TableHeader.displayName = "TableHeader" | ||
|
||
const TableBody = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tbody | ||
ref={ref} | ||
className={cn("[&_tr:last-child]:border-0", className)} | ||
{...props} | ||
/> | ||
)) | ||
TableBody.displayName = "TableBody" | ||
|
||
const TableFooter = React.forwardRef< | ||
HTMLTableSectionElement, | ||
React.HTMLAttributes<HTMLTableSectionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tfoot | ||
ref={ref} | ||
className={cn( | ||
"border-t bg-muted/50 font-medium [&>tr]:last:border-b-0", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
TableFooter.displayName = "TableFooter" | ||
|
||
const TableRow = React.forwardRef< | ||
HTMLTableRowElement, | ||
React.HTMLAttributes<HTMLTableRowElement> | ||
>(({ className, ...props }, ref) => ( | ||
<tr | ||
ref={ref} | ||
className={cn( | ||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
TableRow.displayName = "TableRow" | ||
|
||
const TableHead = React.forwardRef< | ||
HTMLTableCellElement, | ||
React.ThHTMLAttributes<HTMLTableCellElement> | ||
>(({ className, ...props }, ref) => ( | ||
<th | ||
ref={ref} | ||
className={cn( | ||
"h-10 px-2 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
TableHead.displayName = "TableHead" | ||
|
||
const TableCell = React.forwardRef< | ||
HTMLTableCellElement, | ||
React.TdHTMLAttributes<HTMLTableCellElement> | ||
>(({ className, ...props }, ref) => ( | ||
<td | ||
ref={ref} | ||
className={cn( | ||
"p-2 align-middle [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
TableCell.displayName = "TableCell" | ||
|
||
const TableCaption = React.forwardRef< | ||
HTMLTableCaptionElement, | ||
React.HTMLAttributes<HTMLTableCaptionElement> | ||
>(({ className, ...props }, ref) => ( | ||
<caption | ||
ref={ref} | ||
className={cn("mt-4 text-sm text-muted-foreground", className)} | ||
{...props} | ||
/> | ||
)) | ||
TableCaption.displayName = "TableCaption" | ||
|
||
export { | ||
Table, | ||
TableHeader, | ||
TableBody, | ||
TableFooter, | ||
TableHead, | ||
TableRow, | ||
TableCell, | ||
TableCaption, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
"use client" | ||
|
||
import * as React from "react" | ||
import * as TabsPrimitive from "@radix-ui/react-tabs" | ||
|
||
import { cn } from "@/lib/utils" | ||
|
||
const Tabs = TabsPrimitive.Root | ||
|
||
const TabsList = React.forwardRef< | ||
React.ElementRef<typeof TabsPrimitive.List>, | ||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> | ||
>(({ className, ...props }, ref) => ( | ||
<TabsPrimitive.List | ||
ref={ref} | ||
className={cn( | ||
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
TabsList.displayName = TabsPrimitive.List.displayName | ||
|
||
const TabsTrigger = React.forwardRef< | ||
React.ElementRef<typeof TabsPrimitive.Trigger>, | ||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger> | ||
>(({ className, ...props }, ref) => ( | ||
<TabsPrimitive.Trigger | ||
ref={ref} | ||
className={cn( | ||
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName | ||
|
||
const TabsContent = React.forwardRef< | ||
React.ElementRef<typeof TabsPrimitive.Content>, | ||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> | ||
>(({ className, ...props }, ref) => ( | ||
<TabsPrimitive.Content | ||
ref={ref} | ||
className={cn( | ||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", | ||
className | ||
)} | ||
{...props} | ||
/> | ||
)) | ||
TabsContent.displayName = TabsPrimitive.Content.displayName | ||
|
||
export { Tabs, TabsList, TabsTrigger, TabsContent } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.