diff --git a/website/package-lock.json b/website/package-lock.json index 71177bf5..348d9fad 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -46,7 +46,6 @@ "react-feature-flags": "^1.0.0", "react-hook-form": "^7.42.1", "react-i18next": "^12.1.4", - "react-table": "^7.8.0", "sharp": "^0.31.3", "swr": "^2.0.0", "tailwindcss": "^3.2.4", @@ -32756,18 +32755,6 @@ } } }, - "node_modules/react-table": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", - "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^16.8.3 || ^17.0.0-0 || ^18.0.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -62197,12 +62184,6 @@ "tslib": "^2.0.0" } }, - "react-table": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/react-table/-/react-table-7.8.0.tgz", - "integrity": "sha512-hNaz4ygkZO4bESeFfnfOft73iBUj8K5oKi1EcSHPAibEydfsX2MyU6Z8KCr3mv3C9Kqqh71U+DhZkFvibbnPbA==", - "requires": {} - }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", diff --git a/website/package.json b/website/package.json index f2499920..40d279fe 100644 --- a/website/package.json +++ b/website/package.json @@ -63,7 +63,6 @@ "react-feature-flags": "^1.0.0", "react-hook-form": "^7.42.1", "react-i18next": "^12.1.4", - "react-table": "^7.8.0", "sharp": "^0.31.3", "swr": "^2.0.0", "tailwindcss": "^3.2.4", diff --git a/website/public/locales/en/leaderboard.json b/website/public/locales/en/leaderboard.json index c2dd0832..06097642 100644 --- a/website/public/locales/en/leaderboard.json +++ b/website/public/locales/en/leaderboard.json @@ -7,5 +7,8 @@ "rank": "Rank", "score": "Score", "user": "User", - "weekly": "Weekly" + "weekly": "Weekly", + "prompt": "Prompts", + "reply": "Replies", + "label": "Labels" } diff --git a/website/src/components/Dashboard/LeaderboardTable.tsx b/website/src/components/Dashboard/LeaderboardWidget.tsx similarity index 50% rename from website/src/components/Dashboard/LeaderboardTable.tsx rename to website/src/components/Dashboard/LeaderboardWidget.tsx index 52cf762b..aa89d585 100644 --- a/website/src/components/Dashboard/LeaderboardTable.tsx +++ b/website/src/components/Dashboard/LeaderboardWidget.tsx @@ -1,11 +1,9 @@ -import { Box, Link, Text, useColorModeValue } from "@chakra-ui/react"; +import { Card, CardBody, Link, Text } from "@chakra-ui/react"; import NextLink from "next/link"; -import { LeaderboardGridCell } from "src/components/LeaderboardGridCell"; +import { LeaderboardTable } from "src/components/LeaderboardTable"; import { LeaderboardTimeFrame } from "src/types/Leaderboard"; -export function LeaderboardTable() { - const backgroundColor = useColorModeValue("white", "gray.700"); - const accentColor = useColorModeValue("gray.200", "gray.900"); +export function LeaderboardWidget() { return (
@@ -17,15 +15,11 @@ export function LeaderboardTable() {
- - - + + + + +
); diff --git a/website/src/components/Dashboard/index.ts b/website/src/components/Dashboard/index.ts index 84a93850..84858345 100644 --- a/website/src/components/Dashboard/index.ts +++ b/website/src/components/Dashboard/index.ts @@ -1,3 +1,3 @@ -export { LeaderboardTable } from "./LeaderboardTable"; +export { LeaderboardWidget } from "./LeaderboardWidget"; export { TaskOption } from "./TaskOption"; export { WelcomeCard } from "./WelcomeCard"; diff --git a/website/src/components/DataTable.tsx b/website/src/components/DataTable.tsx index 466393eb..a8106317 100644 --- a/website/src/components/DataTable.tsx +++ b/website/src/components/DataTable.tsx @@ -1,8 +1,6 @@ import { Box, Button, - Card, - CardBody, Flex, FormControl, FormLabel, @@ -49,6 +47,7 @@ export type DataTableProps = { onFilterChange?: (items: FilterItem[]) => void; disableNext?: boolean; disablePrevious?: boolean; + disablePagination?: boolean; }; export const DataTable = ({ @@ -61,6 +60,7 @@ export const DataTable = ({ onFilterChange, disableNext, disablePrevious, + disablePagination, }: DataTableProps) => { const { getHeaderGroups, getRowModel } = useReactTable({ data, @@ -79,8 +79,8 @@ export const DataTable = ({ onFilterChange(newValues); }; return ( - - + <> + {!disablePagination && ( - - - {caption} - - {getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - ))} - - ))} - - - {getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - ))} - - ))} - -
- - {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} - {(header.column.columnDef as DataTableColumnDef).filterable && ( - value.id === header.id)?.value ?? ""} - onChange={(value) => handleFilterChange({ id: header.id, value })} - label={flexRender(header.column.columnDef.header, header.getContext())} - > - )} - -
{flexRender(cell.column.columnDef.cell, cell.getContext())}
-
-
-
+ )} + + + {caption} + + {getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + ))} + + ))} + +
+ + {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + {(header.column.columnDef as DataTableColumnDef).filterable && ( + value.id === header.id)?.value ?? ""} + onChange={(value) => handleFilterChange({ id: header.id, value })} + label={flexRender(header.column.columnDef.header, header.getContext())} + > + )} + +
{flexRender(cell.column.columnDef.cell, cell.getContext())}
+
+ ); }; diff --git a/website/src/components/LeaderboardGridCell/LeaderboardGridCell.tsx b/website/src/components/LeaderboardGridCell/LeaderboardGridCell.tsx deleted file mode 100644 index 7886784a..00000000 --- a/website/src/components/LeaderboardGridCell/LeaderboardGridCell.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { Table, TableContainer, Tbody, Td, Text, Th, Thead, Tr, useColorModeValue } from "@chakra-ui/react"; -import { useTranslation } from "next-i18next"; -import React, { useMemo } from "react"; -import { useTable } from "react-table"; -import { get } from "src/lib/api"; -import { LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard"; -import useSWRImmutable from "swr/immutable"; - -const getColumns = (t) => [ - { - Header: t("rank"), - accessor: "rank", - style: { width: "90px" }, - }, - { - Header: t("score"), - accessor: "leader_score", - style: { width: "90px" }, - }, - { - Header: t("user"), - accessor: "display_name", - }, -]; - -/** - * Presents a grid of leaderboard entries with more detailed information. - */ -const LeaderboardGridCell = ({ timeFrame }: { timeFrame: LeaderboardTimeFrame }) => { - const { t } = useTranslation(["leaderboard", "common"]); - const { data: reply } = useSWRImmutable(`/api/leaderboard?time_frame=${timeFrame}`, get, { - revalidateOnMount: true, - }); - - const columns = useMemo(() => getColumns(t), [t]); - - const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } = useTable({ - columns, - data: reply?.leaderboard ?? [], - }); - - const backgroundColor = useColorModeValue("white", "gray.800"); - - const lastUpdated = useMemo(() => { - const val = new Date(reply?.last_updated); - return t("last_updated_at", { val, formatParams: { val: { dateStyle: "full", timeStyle: "short" } } }); - }, [t, reply?.last_updated]); - - if (!reply) { - return null; - } - - return ( - - - - {headerGroups.map((headerGroup, idx) => ( - - {headerGroup.headers.map((column) => ( - - ))} - - ))} - - - - {rows.map((row) => { - prepareRow(row); - return ( - - {row.cells.map((cell, idx) => { - return ( - - ); - })} - - ); - })} - -
- {column.render("Header")} -
- {cell.render("Cell")} -
- {lastUpdated} -
- ); -}; - -export { LeaderboardGridCell }; diff --git a/website/src/components/LeaderboardGridCell/index.tsx b/website/src/components/LeaderboardGridCell/index.tsx deleted file mode 100644 index c4657eb6..00000000 --- a/website/src/components/LeaderboardGridCell/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from "./LeaderboardGridCell"; diff --git a/website/src/components/LeaderboardTable/LeaderboardTable.tsx b/website/src/components/LeaderboardTable/LeaderboardTable.tsx new file mode 100644 index 00000000..847e4dc4 --- /dev/null +++ b/website/src/components/LeaderboardTable/LeaderboardTable.tsx @@ -0,0 +1,64 @@ +import { CircularProgress } from "@chakra-ui/react"; +import { createColumnHelper } from "@tanstack/react-table"; +import { useTranslation } from "next-i18next"; +import React, { useMemo } from "react"; +import { get } from "src/lib/api"; +import { LeaderboardEntity, LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard"; +import useSWRImmutable from "swr/immutable"; + +import { DataTable } from "../DataTable"; + +const columnHelper = createColumnHelper(); + +/** + * Presents a grid of leaderboard entries with more detailed information. + */ +export const LeaderboardTable = ({ timeFrame, limit }: { timeFrame: LeaderboardTimeFrame; limit: number }) => { + const { t } = useTranslation("leaderboard"); + const { + data: reply, + isLoading, + error, + } = useSWRImmutable(`/api/leaderboard?time_frame=${timeFrame}&limit=${limit}`, get, { + revalidateOnMount: true, + }); + + const columns = useMemo( + () => [ + columnHelper.accessor("rank", { + header: t("rank"), + }), + columnHelper.accessor("display_name", { + header: t("user"), + }), + columnHelper.accessor("leader_score", { + header: t("score"), + }), + columnHelper.accessor("prompts", { + header: t("prompt"), + }), + columnHelper.accessor((row) => row.replies_assistant + row.replies_prompter, { + header: t("reply"), + }), + columnHelper.accessor((row) => row.labels_full + row.labels_simple, { + header: t("label"), + }), + ], + [t] + ); + + const lastUpdated = useMemo(() => { + const val = new Date(reply?.last_updated); + return t("last_updated_at", { val, formatParams: { val: { dateStyle: "full", timeStyle: "short" } } }); + }, [t, reply?.last_updated]); + + if (isLoading) { + return ; + } + + if (error) { + return Unable to load leaderboard; + } + + return ; +}; diff --git a/website/src/components/LeaderboardTable/index.tsx b/website/src/components/LeaderboardTable/index.tsx new file mode 100644 index 00000000..d8de5feb --- /dev/null +++ b/website/src/components/LeaderboardTable/index.tsx @@ -0,0 +1 @@ +export * from "./LeaderboardTable"; diff --git a/website/src/components/UserTable.tsx b/website/src/components/UserTable.tsx index 5e5828ea..71d4fe44 100644 --- a/website/src/components/UserTable.tsx +++ b/website/src/components/UserTable.tsx @@ -1,4 +1,4 @@ -import { IconButton } from "@chakra-ui/react"; +import { Card, CardBody, IconButton } from "@chakra-ui/react"; import { createColumnHelper } from "@tanstack/react-table"; import { Pencil } from "lucide-react"; import Link from "next/link"; @@ -90,19 +90,21 @@ export const UserTable = memo(function UserTable() { }; return ( - <> - - {error && "Unable to load users."} - + + + + {error && "Unable to load users."} + + ); }); diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts index 47df8584..952858f2 100644 --- a/website/src/lib/oasst_api_client.ts +++ b/website/src/lib/oasst_api_client.ts @@ -262,8 +262,14 @@ export class OasstApiClient { /** * Returns the current leaderboard ranking. */ - async fetch_leaderboard(time_frame: LeaderboardTimeFrame): Promise { - return this.get(`/api/v1/leaderboards/${time_frame}`); + async fetch_leaderboard( + time_frame: LeaderboardTimeFrame, + { limit = 20 }: { limit?: number } + ): Promise { + const params = new URLSearchParams({ + limit: limit.toString(), + }); + return this.get(`/api/v1/leaderboards/${time_frame}?${params.toString()}`); } /** diff --git a/website/src/pages/api/leaderboard.ts b/website/src/pages/api/leaderboard.ts index 1ddf947e..be91e7b4 100644 --- a/website/src/pages/api/leaderboard.ts +++ b/website/src/pages/api/leaderboard.ts @@ -7,7 +7,7 @@ import { LeaderboardTimeFrame } from "src/types/Leaderboard"; */ const handler = withoutRole("banned", async (req, res) => { const time_frame = (req.query.time_frame as LeaderboardTimeFrame) ?? LeaderboardTimeFrame.day; - const info = await oasstApiClient.fetch_leaderboard(time_frame); + const info = await oasstApiClient.fetch_leaderboard(time_frame, { limit: req.query.limit as unknown as number }); res.status(200).json(info); }); diff --git a/website/src/pages/dashboard.tsx b/website/src/pages/dashboard.tsx index 32774f4a..17e04c8a 100644 --- a/website/src/pages/dashboard.tsx +++ b/website/src/pages/dashboard.tsx @@ -2,7 +2,7 @@ import { Flex } from "@chakra-ui/react"; import Head from "next/head"; import { useTranslation } from "next-i18next"; import { useEffect, useMemo, useState } from "react"; -import { LeaderboardTable, TaskOption, WelcomeCard } from "src/components/Dashboard"; +import { LeaderboardWidget, TaskOption, WelcomeCard } from "src/components/Dashboard"; import { getDashboardLayout } from "src/components/Layout"; import { TaskCategory } from "src/components/Tasks/TaskTypes"; import { get } from "src/lib/api"; @@ -42,7 +42,7 @@ const Dashboard = () => { - + ); diff --git a/website/src/pages/leaderboard.tsx b/website/src/pages/leaderboard.tsx index 7bb8e025..18f64bac 100644 --- a/website/src/pages/leaderboard.tsx +++ b/website/src/pages/leaderboard.tsx @@ -1,9 +1,9 @@ -import { Box, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; +import { Box, Card, CardBody, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react"; import Head from "next/head"; import { useTranslation } from "next-i18next"; import { getDashboardLayout } from "src/components/Layout"; -import { LeaderboardGridCell } from "src/components/LeaderboardGridCell"; export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props"; +import { LeaderboardTable } from "src/components/LeaderboardTable"; import { LeaderboardTimeFrame } from "src/types/Leaderboard"; const Leaderboard = () => { @@ -18,29 +18,33 @@ const Leaderboard = () => { {t("leaderboard:leaderboard")} - - - {t("leaderboard:daily")} - {t("leaderboard:weekly")} - {t("leaderboard:monthly")} - {t("leaderboard:overall")} - + + + + + {t("leaderboard:daily")} + {t("leaderboard:weekly")} + {t("leaderboard:monthly")} + {t("leaderboard:overall")} + - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + );