diff --git a/app/leaderboard/action.ts b/app/leaderboard/action.ts new file mode 100644 index 0000000..0c13364 --- /dev/null +++ b/app/leaderboard/action.ts @@ -0,0 +1,29 @@ +"use server"; +import { supabaseServer } from "@/utils/supabase/server"; + +const supabase = supabaseServer(); + +type LeaderboardProp = { + page?: number; +}; + +export const getLeaderboard = async ({ page = 1 }: LeaderboardProp) => { + try { + const itemsPerPage = 20; + const from = (page - 1) * itemsPerPage; + const to = from + itemsPerPage - 1; + + const { data, error } = await supabase + .from("recent_users") + .select("*") + .neq("rating", "-0.1") + .order("rating", { ascending: false }) + .range(from, to); + + if (error) throw error; + + return data; + } catch (error) { + throw error; + } +}; diff --git a/app/leaderboard/page.tsx b/app/leaderboard/page.tsx new file mode 100644 index 0000000..a372495 --- /dev/null +++ b/app/leaderboard/page.tsx @@ -0,0 +1,130 @@ +"use client"; +import React, { useEffect, useState } from "react"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + Pagination, + PaginationContent, + PaginationEllipsis, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from "@/components/ui/pagination"; + +import { getLeaderboard } from "./action"; +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; +import Link from "next/link"; +import { Skeleton } from "@/components/ui/skeleton"; + +interface LeaderboardProp { + avatar_url: string; + username: string; + rating: number; +} + +export default function Leaderboard() { + const [leaderboard, setLeaderboard] = useState([]); + const [page, setPage] = useState(1); + + useEffect(() => { + getLeaderboard({ page }).then((data) => setLeaderboard(data)); + }, [page]); + + const handlePrevious = () => { + if (page > 1) { + setPage(page - 1); + setLeaderboard([]); + } + }; + + const handleNext = () => { + setPage(page + 1); + setLeaderboard([]); + }; + + return ( +
+

Leaderboard

+ + + + Rank + Avatar + Username + Rating + + + {leaderboard.length > 0 ? ( + + {leaderboard.map((user, index) => ( + + {(page - 1) * 20 + index + 1} + + + + CN + + + + + {user.username} + + + {user.rating} + + ))} + + ) : ( + + {Array.from({ length: 5 }, (_, index) => ( + + + + + + + + + + + + + + + ))} + + )} +
+ + + Previous + + + + {page} + + + + + Next + + +
+ ); +} diff --git a/components/ui/pagination.tsx b/components/ui/pagination.tsx new file mode 100644 index 0000000..7715f05 --- /dev/null +++ b/components/ui/pagination.tsx @@ -0,0 +1,121 @@ +import * as React from "react" +import { + ChevronLeftIcon, + ChevronRightIcon, + DotsHorizontalIcon, +} from "@radix-ui/react-icons" + +import { cn } from "@/lib/utils" +import { ButtonProps, buttonVariants } from "@/components/ui/button" + +const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => ( +