mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-27 16:10:30 +08:00
implement trollboard UI (#1301)
* implement trollboard UI * remove unneeded code * add link to user * user link in leaderboard
This commit is contained in:
Generated
+14
-14
@@ -12332,11 +12332,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-table": {
|
||||
"version": "8.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.7.6.tgz",
|
||||
"integrity": "sha512-/QijmMFeP7wDLBnr0MQ/5MlbXePbIL/1nOtkxBC9zvmBu4gDKJEDBqipUyM7Wc/iBpSd0IFyqBlvZvTPD9FYDA==",
|
||||
"version": "8.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.7.9.tgz",
|
||||
"integrity": "sha512-6MbbQn5AupSOkek1+6IYu+1yZNthAKTRZw9tW92Vi6++iRrD1GbI3lKTjJalf8lEEKOqapPzQPE20nywu0PjCA==",
|
||||
"dependencies": {
|
||||
"@tanstack/table-core": "8.7.6"
|
||||
"@tanstack/table-core": "8.7.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -12351,9 +12351,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/table-core": {
|
||||
"version": "8.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.7.6.tgz",
|
||||
"integrity": "sha512-sqiNTMzB6cpyL8DFH6/VqW48SwiflLqxQqYpo2wNock7rdVGvlm0BLNI8vZUJbr1+fmmWmHwBvi5OMgZw8n1DA==",
|
||||
"version": "8.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.7.9.tgz",
|
||||
"integrity": "sha512-4RkayPMV1oS2SKDXfQbFoct1w5k+pvGpmX18tCXMofK/VDRdA2hhxfsQlMvsJ4oTX8b0CI4Y3GDKn5T425jBCw==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -47665,17 +47665,17 @@
|
||||
}
|
||||
},
|
||||
"@tanstack/react-table": {
|
||||
"version": "8.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.7.6.tgz",
|
||||
"integrity": "sha512-/QijmMFeP7wDLBnr0MQ/5MlbXePbIL/1nOtkxBC9zvmBu4gDKJEDBqipUyM7Wc/iBpSd0IFyqBlvZvTPD9FYDA==",
|
||||
"version": "8.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.7.9.tgz",
|
||||
"integrity": "sha512-6MbbQn5AupSOkek1+6IYu+1yZNthAKTRZw9tW92Vi6++iRrD1GbI3lKTjJalf8lEEKOqapPzQPE20nywu0PjCA==",
|
||||
"requires": {
|
||||
"@tanstack/table-core": "8.7.6"
|
||||
"@tanstack/table-core": "8.7.9"
|
||||
}
|
||||
},
|
||||
"@tanstack/table-core": {
|
||||
"version": "8.7.6",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.7.6.tgz",
|
||||
"integrity": "sha512-sqiNTMzB6cpyL8DFH6/VqW48SwiflLqxQqYpo2wNock7rdVGvlm0BLNI8vZUJbr1+fmmWmHwBvi5OMgZw8n1DA=="
|
||||
"version": "8.7.9",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.7.9.tgz",
|
||||
"integrity": "sha512-4RkayPMV1oS2SKDXfQbFoct1w5k+pvGpmX18tCXMofK/VDRdA2hhxfsQlMvsJ4oTX8b0CI4Y3GDKn5T425jBCw=="
|
||||
},
|
||||
"@testing-library/dom": {
|
||||
"version": "8.19.1",
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
"users": "Users",
|
||||
"users_dashboard": "Users Dashboard",
|
||||
"status": "Status",
|
||||
"status_dashboard": "Status Dashboard"
|
||||
"status_dashboard": "Status Dashboard",
|
||||
"trollboard": "Trollboard"
|
||||
}
|
||||
|
||||
@@ -74,6 +74,11 @@ export const getAdminLayout = (page: React.ReactElement) => (
|
||||
pathname: "/admin",
|
||||
icon: Users,
|
||||
},
|
||||
{
|
||||
labelID: "trollboard",
|
||||
pathname: "/admin/trollboard",
|
||||
icon: BarChart2,
|
||||
},
|
||||
{
|
||||
labelID: "status",
|
||||
pathname: "/admin/status",
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
import { Box, CircularProgress, Flex, useColorModeValue, useToken } from "@chakra-ui/react";
|
||||
import { Box, CircularProgress, Flex, Link, useColorModeValue } from "@chakra-ui/react";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { MoreHorizontal } from "lucide-react";
|
||||
import NextLink from "next/link";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import { get } from "src/lib/api";
|
||||
import { colors } from "src/styles/Theme/colors";
|
||||
import React, { useMemo } from "react";
|
||||
import { LeaderboardEntity, LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
|
||||
import { DataTable, DataTableColumnDef, DataTableRowPropsCallback } from "../DataTable";
|
||||
|
||||
import { DataTable, DataTableColumnDef } from "../DataTable";
|
||||
import { useBoardPagination } from "./useBoardPagination";
|
||||
import { useBoardRowProps } from "./useBoardRowProps";
|
||||
import { useFetchBoard } from "./useFetchBoard";
|
||||
type WindowLeaderboardEntity = LeaderboardEntity & { isSpaceRow?: boolean };
|
||||
|
||||
const columnHelper = createColumnHelper<WindowLeaderboardEntity>();
|
||||
@@ -34,10 +35,13 @@ export const LeaderboardTable = ({
|
||||
data: reply,
|
||||
isLoading,
|
||||
error,
|
||||
} = useSWRImmutable<LeaderboardReply & { user_stats_window?: LeaderboardReply["leaderboard"] }>(
|
||||
`/api/leaderboard?time_frame=${timeFrame}&limit=${limit}&includeUserStats=${!hideCurrentUserRanking}`,
|
||||
get
|
||||
lastUpdated,
|
||||
} = useFetchBoard<LeaderboardReply & { user_stats_window?: LeaderboardReply["leaderboard"] }>(
|
||||
`/api/leaderboard?time_frame=${timeFrame}&limit=${limit}&includeUserStats=${!hideCurrentUserRanking}`
|
||||
);
|
||||
const { data: session } = useSession();
|
||||
|
||||
const isAdmin = session?.user?.role === "admin";
|
||||
const columns: DataTableColumnDef<WindowLeaderboardEntity>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
@@ -49,6 +53,14 @@ export const LeaderboardTable = ({
|
||||
},
|
||||
columnHelper.accessor("display_name", {
|
||||
header: t("user"),
|
||||
cell: ({ getValue, row }) =>
|
||||
isAdmin ? (
|
||||
<Link as={NextLink} href={`/admin/manage_user/${row.original.user_id}`}>
|
||||
{getValue()}
|
||||
</Link>
|
||||
) : (
|
||||
getValue()
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("leader_score", {
|
||||
header: t("score"),
|
||||
@@ -63,40 +75,32 @@ export const LeaderboardTable = ({
|
||||
header: t("label"),
|
||||
}),
|
||||
],
|
||||
[t]
|
||||
[isAdmin, 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]);
|
||||
|
||||
const [page, setPage] = useState(1);
|
||||
const {
|
||||
data: paginatedData,
|
||||
end,
|
||||
...pagnationProps
|
||||
} = useBoardPagination({ rowPerPage, data: reply?.leaderboard, limit });
|
||||
const data: WindowLeaderboardEntity[] = useMemo(() => {
|
||||
if (!reply) {
|
||||
return [];
|
||||
}
|
||||
const start = (page - 1) * rowPerPage;
|
||||
const end = start + rowPerPage;
|
||||
const leaderBoardEntities = reply.leaderboard.slice(start, end);
|
||||
if (hideCurrentUserRanking || !reply.user_stats_window) {
|
||||
return leaderBoardEntities;
|
||||
if (hideCurrentUserRanking || !reply?.user_stats_window) {
|
||||
return paginatedData;
|
||||
}
|
||||
const userStatsWindow: WindowLeaderboardEntity[] = reply.user_stats_window;
|
||||
const userStats = userStatsWindow.find((stats) => stats.highlighted);
|
||||
if (userStats.rank > end) {
|
||||
leaderBoardEntities.push(
|
||||
if (userStats && userStats.rank > end) {
|
||||
paginatedData.push(
|
||||
{ isSpaceRow: true } as WindowLeaderboardEntity,
|
||||
...reply.user_stats_window.filter(
|
||||
(stats) =>
|
||||
leaderBoardEntities.findIndex((leaderBoardEntity) => leaderBoardEntity.user_id === stats.user_id) === -1
|
||||
(stats) => paginatedData.findIndex((leaderBoardEntity) => leaderBoardEntity.user_id === stats.user_id) === -1
|
||||
) // filter to avoid duplicated row
|
||||
);
|
||||
}
|
||||
return leaderBoardEntities;
|
||||
}, [page, rowPerPage, reply, hideCurrentUserRanking]);
|
||||
return paginatedData;
|
||||
}, [hideCurrentUserRanking, reply?.user_stats_window, end, paginatedData]);
|
||||
|
||||
const rowProps = useLeaderboardRowProps();
|
||||
const rowProps = useBoardRowProps<WindowLeaderboardEntity>();
|
||||
|
||||
if (isLoading) {
|
||||
return <CircularProgress isIndeterminate></CircularProgress>;
|
||||
@@ -106,19 +110,13 @@ export const LeaderboardTable = ({
|
||||
return <span>Unable to load leaderboard</span>;
|
||||
}
|
||||
|
||||
const maxPage = Math.ceil(reply.leaderboard.length / rowPerPage);
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
<DataTable<WindowLeaderboardEntity>
|
||||
data={data}
|
||||
columns={columns}
|
||||
caption={lastUpdated}
|
||||
disablePagination={limit <= rowPerPage}
|
||||
disableNext={page >= maxPage}
|
||||
disablePrevious={page === 1}
|
||||
onNextClick={() => setPage((p) => p + 1)}
|
||||
onPreviousClick={() => setPage((p) => p - 1)}
|
||||
rowProps={rowProps}
|
||||
{...pagnationProps}
|
||||
></DataTable>
|
||||
);
|
||||
};
|
||||
@@ -131,32 +129,3 @@ const SpaceRow = () => {
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
const useLeaderboardRowProps = () => {
|
||||
const borderColor = useToken("colors", useColorModeValue(colors.light.active, colors.dark.active));
|
||||
return useCallback<DataTableRowPropsCallback<WindowLeaderboardEntity>>(
|
||||
(row) => {
|
||||
const rowData = row.original;
|
||||
return rowData.highlighted
|
||||
? {
|
||||
sx: {
|
||||
// https://stackoverflow.com/questions/37963524/how-to-apply-border-radius-to-tr-in-bootstrap
|
||||
position: "relative",
|
||||
"td:first-of-type:before": {
|
||||
borderLeft: `6px solid ${borderColor}`,
|
||||
content: `""`,
|
||||
display: "block",
|
||||
width: "10px",
|
||||
height: "100%",
|
||||
left: 0,
|
||||
top: 0,
|
||||
borderRadius: "6px 0 0 6px",
|
||||
position: "absolute",
|
||||
},
|
||||
},
|
||||
}
|
||||
: {};
|
||||
},
|
||||
[borderColor]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
import { Box, CircularProgress, Flex, Link } from "@chakra-ui/react";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { ThumbsDown, ThumbsUp } from "lucide-react";
|
||||
import NextLink from "next/link";
|
||||
import { FetchTrollBoardResponse, TrollboardEntity, TrollboardTimeFrame } from "src/types/Trollboard";
|
||||
|
||||
import { DataTable } from "../DataTable";
|
||||
import { useBoardPagination } from "./useBoardPagination";
|
||||
import { useBoardRowProps } from "./useBoardRowProps";
|
||||
import { useFetchBoard } from "./useFetchBoard";
|
||||
const columnHelper = createColumnHelper<TrollboardEntity>();
|
||||
|
||||
const toPercentage = (num: number) => `${Math.round(num * 100)}%`;
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor("rank", {}),
|
||||
columnHelper.accessor("display_name", {
|
||||
header: "Display name",
|
||||
cell: ({ getValue, row }) => (
|
||||
<Link as={NextLink} href={`/admin/manage_user/${row.original.user_id}`}>
|
||||
{getValue()}
|
||||
</Link>
|
||||
),
|
||||
}),
|
||||
columnHelper.accessor("troll_score", {
|
||||
header: "Troll score",
|
||||
}),
|
||||
columnHelper.accessor("red_flags", {
|
||||
header: "Red flags",
|
||||
}),
|
||||
columnHelper.accessor((row) => [row.upvotes, row.downvotes] as const, {
|
||||
id: "vote",
|
||||
cell: ({ getValue }) => {
|
||||
const [up, down] = getValue();
|
||||
return (
|
||||
<Flex gap={2} justifyItems="center" alignItems="center">
|
||||
<ThumbsUp></ThumbsUp>
|
||||
{up}
|
||||
<ThumbsDown></ThumbsDown>
|
||||
{down}
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor((row) => row.spam + row.spam_prompts, {
|
||||
header: "Spam",
|
||||
}),
|
||||
columnHelper.accessor("lang_mismach", {
|
||||
header: "Lang mismach",
|
||||
}),
|
||||
columnHelper.accessor("not_appropriate", {
|
||||
header: "Not appropriate",
|
||||
}),
|
||||
columnHelper.accessor("pii", {}),
|
||||
columnHelper.accessor("hate_speech", {
|
||||
header: "Hate speech",
|
||||
}),
|
||||
columnHelper.accessor("sexual_content", {
|
||||
header: "Sexual Content",
|
||||
}),
|
||||
columnHelper.accessor("political_content", {
|
||||
header: "Political Content",
|
||||
}),
|
||||
columnHelper.accessor("quality", {
|
||||
cell: ({ getValue }) => toPercentage(getValue()),
|
||||
}),
|
||||
columnHelper.accessor("helpfulness", {
|
||||
cell: ({ getValue }) => toPercentage(getValue()),
|
||||
}),
|
||||
columnHelper.accessor("humor", {
|
||||
cell: ({ getValue }) => toPercentage(getValue()),
|
||||
}),
|
||||
columnHelper.accessor("violence", {
|
||||
cell: ({ getValue }) => toPercentage(getValue()),
|
||||
}),
|
||||
columnHelper.accessor("toxicity", {
|
||||
cell: ({ getValue }) => toPercentage(getValue()),
|
||||
}),
|
||||
];
|
||||
|
||||
export const TrollboardTable = ({
|
||||
limit,
|
||||
rowPerPage,
|
||||
timeFrame,
|
||||
}: {
|
||||
timeFrame: TrollboardTimeFrame;
|
||||
limit: number;
|
||||
rowPerPage: number;
|
||||
}) => {
|
||||
const {
|
||||
data: trollboardRes,
|
||||
isLoading,
|
||||
error,
|
||||
lastUpdated,
|
||||
} = useFetchBoard<FetchTrollBoardResponse>(`/api/admin/trollboard?time_frame=${timeFrame}&limit=${limit}`);
|
||||
|
||||
const { data, ...paginationProps } = useBoardPagination({ rowPerPage, data: trollboardRes?.trollboard, limit });
|
||||
const rowProps = useBoardRowProps<TrollboardEntity>();
|
||||
if (isLoading) {
|
||||
return <CircularProgress isIndeterminate></CircularProgress>;
|
||||
}
|
||||
|
||||
if (error) {
|
||||
return <span>Unable to load leaderboard</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
"th,td": {
|
||||
px: 2,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DataTable<TrollboardEntity>
|
||||
data={data}
|
||||
columns={columns}
|
||||
caption={lastUpdated}
|
||||
rowProps={rowProps}
|
||||
{...paginationProps}
|
||||
></DataTable>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { useState } from "react";
|
||||
|
||||
export const useBoardPagination = <T>({
|
||||
rowPerPage,
|
||||
limit,
|
||||
...res
|
||||
}: {
|
||||
rowPerPage: number;
|
||||
data?: T[];
|
||||
limit: number;
|
||||
}) => {
|
||||
const data = res.data || [];
|
||||
const [page, setPage] = useState(1);
|
||||
const maxPage = data ? Math.ceil(data.length / rowPerPage) : 0;
|
||||
const disablePagination = limit <= rowPerPage;
|
||||
const disableNext = page >= maxPage;
|
||||
const disablePrevious = page === 1;
|
||||
const onNextClick = () => setPage((p) => p + 1);
|
||||
const onPreviousClick = () => setPage((p) => p - 1);
|
||||
const start = (page - 1) * rowPerPage;
|
||||
const end = start + rowPerPage;
|
||||
const entities = data.slice(start, end);
|
||||
|
||||
return {
|
||||
page,
|
||||
data: entities,
|
||||
end,
|
||||
disablePrevious,
|
||||
disableNext,
|
||||
disablePagination,
|
||||
onNextClick,
|
||||
onPreviousClick,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
import { useColorModeValue, useToken } from "@chakra-ui/react";
|
||||
import { useCallback } from "react";
|
||||
import { colors } from "src/styles/Theme/colors";
|
||||
|
||||
import { DataTableRowPropsCallback } from "../DataTable";
|
||||
|
||||
export const useBoardRowProps = <T extends { highlighted: boolean }>() => {
|
||||
const borderColor = useToken("colors", useColorModeValue(colors.light.active, colors.dark.active));
|
||||
return useCallback<DataTableRowPropsCallback<T>>(
|
||||
(row) => {
|
||||
const rowData = row.original;
|
||||
return rowData.highlighted
|
||||
? {
|
||||
sx: {
|
||||
// https://stackoverflow.com/questions/37963524/how-to-apply-border-radius-to-tr-in-bootstrap
|
||||
position: "relative",
|
||||
"td:first-of-type:before": {
|
||||
borderLeft: `6px solid ${borderColor}`,
|
||||
content: `""`,
|
||||
display: "block",
|
||||
width: "10px",
|
||||
height: "100%",
|
||||
left: 0,
|
||||
top: 0,
|
||||
borderRadius: "6px 0 0 6px",
|
||||
position: "absolute",
|
||||
},
|
||||
},
|
||||
}
|
||||
: {};
|
||||
},
|
||||
[borderColor]
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { useMemo } from "react";
|
||||
import { get } from "src/lib/api";
|
||||
import useSWRImmutable from "swr/immutable";
|
||||
|
||||
export const useFetchBoard = <T extends { last_updated: string }>(url: string) => {
|
||||
const { t } = useTranslation("leaderboard");
|
||||
const res = useSWRImmutable<T>(url, get);
|
||||
|
||||
const lastUpdated = useMemo(() => {
|
||||
const val = res.data ? new Date(res.data.last_updated) : new Date();
|
||||
return t("last_updated_at", { val, formatParams: { val: { dateStyle: "full", timeStyle: "short" } } });
|
||||
}, [res.data, t]);
|
||||
|
||||
return {
|
||||
...res,
|
||||
lastUpdated,
|
||||
};
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { EmojiOp, Message } from "src/types/Conversation";
|
||||
import { LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard";
|
||||
import type { AvailableTasks } from "src/types/Task";
|
||||
import { FetchTrollBoardResponse, TrollboardTimeFrame } from "src/types/Trollboard";
|
||||
import type { BackendUser, BackendUserCore, FetchUsersParams, FetchUsersResponse } from "src/types/Users";
|
||||
|
||||
export class OasstError {
|
||||
@@ -350,4 +351,10 @@ export class OasstApiClient {
|
||||
fetch_frontend_user(user: BackendUserCore) {
|
||||
return this.get<BackendUser>(`/api/v1/frontend_users/${user.auth_method}/${user.id}`);
|
||||
}
|
||||
|
||||
fetch_trollboard(time_frame: TrollboardTimeFrame, { limit }: { limit?: number }) {
|
||||
return this.get<FetchTrollBoardResponse>(`/api/v1/trollboards/${time_frame}`, {
|
||||
max_count: limit,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
import { Box, Card, CardBody, Heading, Tab, TabList, TabPanel, TabPanels, Tabs } from "@chakra-ui/react";
|
||||
import Head from "next/head";
|
||||
import { useTranslation } from "next-i18next";
|
||||
export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props";
|
||||
import { AdminArea } from "src/components/AdminArea";
|
||||
import { getAdminLayout } from "src/components/Layout";
|
||||
import { TrollboardTable } from "src/components/LeaderboardTable/TrollboardTable";
|
||||
import { TrollboardTimeFrame } from "src/types/Trollboard";
|
||||
|
||||
const Leaderboard = () => {
|
||||
const { t } = useTranslation(["leaderboard", "common"]);
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{`Trollboard - ${t("common:title")}`}</title>
|
||||
<meta name="description" content="Admin Trollboard" charSet="UTF-8" />
|
||||
</Head>
|
||||
<AdminArea>
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Heading fontSize="2xl" fontWeight="bold" pb="4">
|
||||
{t("leaderboard")}
|
||||
</Heading>
|
||||
<Card>
|
||||
<CardBody>
|
||||
<Tabs isFitted isLazy>
|
||||
<TabList mb={4}>
|
||||
<Tab>{t("daily")}</Tab>
|
||||
<Tab>{t("weekly")}</Tab>
|
||||
<Tab>{t("monthly")}</Tab>
|
||||
<Tab>{t("overall")}</Tab>
|
||||
</TabList>
|
||||
<TabPanels>
|
||||
<TabPanel p="0">
|
||||
<TrollboardTable timeFrame={TrollboardTimeFrame.day} limit={100} rowPerPage={20} />
|
||||
</TabPanel>
|
||||
<TabPanel p="0">
|
||||
<TrollboardTable timeFrame={TrollboardTimeFrame.week} limit={100} rowPerPage={20} />
|
||||
</TabPanel>
|
||||
<TabPanel p="0">
|
||||
<TrollboardTable timeFrame={TrollboardTimeFrame.month} limit={100} rowPerPage={20} />
|
||||
</TabPanel>
|
||||
<TabPanel p="0">
|
||||
<TrollboardTable timeFrame={TrollboardTimeFrame.total} limit={100} rowPerPage={20} />
|
||||
</TabPanel>
|
||||
</TabPanels>
|
||||
</Tabs>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Box>
|
||||
</AdminArea>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Leaderboard.getLayout = getAdminLayout;
|
||||
|
||||
export default Leaderboard;
|
||||
@@ -0,0 +1,13 @@
|
||||
import { withRole } from "src/lib/auth";
|
||||
import { createApiClient } from "src/lib/oasst_client_factory";
|
||||
import { TrollboardTimeFrame } from "src/types/Trollboard";
|
||||
|
||||
export default withRole("admin", async (req, res, token) => {
|
||||
const client = await createApiClient(token);
|
||||
|
||||
const trollboard = await client.fetch_trollboard(req.query.time_frame as TrollboardTimeFrame, {
|
||||
limit: req.query.limit as unknown as number,
|
||||
});
|
||||
|
||||
return res.status(200).json(trollboard);
|
||||
});
|
||||
@@ -4,7 +4,7 @@ export interface LeaderboardEntry {
|
||||
score: number;
|
||||
}
|
||||
|
||||
export const enum LeaderboardTimeFrame {
|
||||
export enum LeaderboardTimeFrame {
|
||||
day = "day",
|
||||
week = "week",
|
||||
month = "month",
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
export enum TrollboardTimeFrame {
|
||||
day = "day",
|
||||
week = "week",
|
||||
month = "month",
|
||||
total = "total",
|
||||
}
|
||||
|
||||
export type FetchTrollBoardResponse = {
|
||||
time_frame: TrollboardTimeFrame;
|
||||
last_updated: string;
|
||||
trollboard: TrollboardEntity[];
|
||||
};
|
||||
|
||||
export type TrollboardEntity = {
|
||||
rank: number;
|
||||
user_id: string;
|
||||
highlighted: boolean;
|
||||
username: string;
|
||||
auth_method: string;
|
||||
display_name: string;
|
||||
last_activity_date: string | null;
|
||||
troll_score: number;
|
||||
base_date: string;
|
||||
modified_date: string;
|
||||
red_flags: number;
|
||||
upvotes: number;
|
||||
downvotes: number;
|
||||
spam_prompts: 0;
|
||||
quality: number | null;
|
||||
humor: number | null;
|
||||
toxicity: number | null;
|
||||
violence: number | null;
|
||||
helpfulness: number | null;
|
||||
spam: number;
|
||||
lang_mismach: number;
|
||||
not_appropriate: number;
|
||||
pii: number;
|
||||
hate_speech: number;
|
||||
sexual_content: number;
|
||||
political_content: number;
|
||||
};
|
||||
Reference in New Issue
Block a user