mirror of
https://github.com/wassname/Open-Assistant.git
synced 2026-06-29 16:30:24 +08:00
Trollboard expandable (#1354)
* wip * hide necessary column in trollboard * remove console.log * fix build * clean up * remove commented code
This commit is contained in:
+23
-3
@@ -23,13 +23,23 @@ import {
|
||||
Tr,
|
||||
useDisclosure,
|
||||
} from "@chakra-ui/react";
|
||||
import { Cell, ColumnDef, flexRender, getCoreRowModel, Row, useReactTable } from "@tanstack/react-table";
|
||||
import {
|
||||
Cell,
|
||||
ColumnDef,
|
||||
ExpandedState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getExpandedRowModel,
|
||||
Row,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { Filter } from "lucide-react";
|
||||
import { useTranslation } from "next-i18next";
|
||||
import { ChangeEvent, ReactNode } from "react";
|
||||
import { ChangeEvent, ReactNode, useState } from "react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
|
||||
export type DataTableColumnDef<T> = ColumnDef<T> & {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type DataTableColumnDef<T> = ColumnDef<T, any> & {
|
||||
filterable?: boolean;
|
||||
span?: number | ((cell: Cell<T, unknown>) => number | undefined);
|
||||
};
|
||||
@@ -54,6 +64,7 @@ export type DataTableProps<T> = {
|
||||
disablePrevious?: boolean;
|
||||
disablePagination?: boolean;
|
||||
rowProps?: TableRowProps | DataTableRowPropsCallback<T>;
|
||||
getSubRows?: (row: T) => T[] | undefined;
|
||||
};
|
||||
|
||||
export const DataTable = <T,>({
|
||||
@@ -68,12 +79,21 @@ export const DataTable = <T,>({
|
||||
disablePrevious,
|
||||
disablePagination,
|
||||
rowProps,
|
||||
getSubRows,
|
||||
}: DataTableProps<T>) => {
|
||||
const { t } = useTranslation("leaderboard");
|
||||
const [expanded, setExpanded] = useState<ExpandedState>({});
|
||||
|
||||
const { getHeaderGroups, getRowModel } = useReactTable<T>({
|
||||
data,
|
||||
columns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getExpandedRowModel: getExpandedRowModel(),
|
||||
state: {
|
||||
expanded,
|
||||
},
|
||||
getSubRows,
|
||||
onExpandedChange: setExpanded,
|
||||
});
|
||||
|
||||
const handleFilterChange = (value: FilterItem) => {
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Card, CardBody, Flex } from "@chakra-ui/react";
|
||||
import { Cell, CellContext } from "@tanstack/react-table";
|
||||
import { ChevronDown, ChevronRight } from "lucide-react";
|
||||
|
||||
type ExpandableRow<T> = Omit<T, "shouldExpand"> & {
|
||||
shouldExpand?: boolean;
|
||||
};
|
||||
|
||||
export const createJsonExpandRowModel = <T,>() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const renderCell = ({ row, getValue }: CellContext<ExpandableRow<T>, any>) => {
|
||||
if (!row.original.shouldExpand) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { shouldExpand, ...res } = row.original;
|
||||
return (
|
||||
<Card variant="json">
|
||||
<CardBody>
|
||||
<pre>{JSON.stringify(res, null, 2)}</pre>
|
||||
</CardBody>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex alignItems="center">
|
||||
{row.getCanExpand() ? (
|
||||
<button
|
||||
{...{
|
||||
onClick: row.getToggleExpandedHandler(),
|
||||
style: { cursor: "pointer" },
|
||||
}}
|
||||
>
|
||||
{row.getIsExpanded() ? <ChevronDown /> : <ChevronRight />}
|
||||
</button>
|
||||
) : null}{" "}
|
||||
{getValue()}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const span = (cell: Cell<ExpandableRow<T>, any>) =>
|
||||
cell.row.original.shouldExpand ? undefined : cell.row.getVisibleCells().length;
|
||||
|
||||
const getSubRows = (row: ExpandableRow<T>) =>
|
||||
row.shouldExpand
|
||||
? [
|
||||
{
|
||||
...row,
|
||||
shouldExpand: false,
|
||||
},
|
||||
]
|
||||
: undefined;
|
||||
|
||||
const toExpandable = function (arr: T[] | undefined, val = true): ExpandableRow<T>[] {
|
||||
return !arr ? [] : arr.map((element) => ({ ...element, shouldExpand: val }));
|
||||
};
|
||||
|
||||
return { renderCell, span, getSubRows, toExpandable };
|
||||
};
|
||||
@@ -2,19 +2,20 @@ import { Box, CircularProgress, Flex, Link, useColorModeValue } from "@chakra-ui
|
||||
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, { useMemo } from "react";
|
||||
import { useHasRole } from "src/hooks/auth/useHasRole";
|
||||
import { LeaderboardEntity, LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard";
|
||||
|
||||
import { DataTable, DataTableColumnDef } from "../DataTable";
|
||||
import { DataTable, DataTableColumnDef } from "../DataTable/DataTable";
|
||||
import { createJsonExpandRowModel } from "../DataTable/jsonExpandRowModel";
|
||||
import { useBoardPagination } from "./useBoardPagination";
|
||||
import { useBoardRowProps } from "./useBoardRowProps";
|
||||
import { useFetchBoard } from "./useFetchBoard";
|
||||
type WindowLeaderboardEntity = LeaderboardEntity & { isSpaceRow?: boolean };
|
||||
|
||||
const columnHelper = createColumnHelper<WindowLeaderboardEntity>();
|
||||
|
||||
const jsonExpandRowModel = createJsonExpandRowModel<WindowLeaderboardEntity>();
|
||||
/**
|
||||
* Presents a grid of leaderboard entries with more detailed information.
|
||||
*/
|
||||
@@ -39,17 +40,24 @@ export const LeaderboardTable = ({
|
||||
} = 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 isAdmin = useHasRole("admin");
|
||||
|
||||
const columns: DataTableColumnDef<WindowLeaderboardEntity>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
...columnHelper.accessor("rank", {
|
||||
header: t("rank"),
|
||||
cell: ({ row, getValue }) => (row.original.isSpaceRow ? <SpaceRow></SpaceRow> : getValue()),
|
||||
cell: (ctx) =>
|
||||
ctx.row.original.isSpaceRow ? (
|
||||
<SpaceRow></SpaceRow>
|
||||
) : isAdmin ? (
|
||||
jsonExpandRowModel.renderCell(ctx)
|
||||
) : (
|
||||
ctx.getValue()
|
||||
),
|
||||
}),
|
||||
span: (cell) => (cell.row.original.isSpaceRow ? 6 : undefined),
|
||||
span: (cell) => (cell.row.original.isSpaceRow ? 6 : jsonExpandRowModel.span(cell)),
|
||||
},
|
||||
columnHelper.accessor("display_name", {
|
||||
header: t("user"),
|
||||
@@ -82,17 +90,17 @@ export const LeaderboardTable = ({
|
||||
data: paginatedData,
|
||||
end,
|
||||
...pagnationProps
|
||||
} = useBoardPagination({ rowPerPage, data: reply?.leaderboard, limit });
|
||||
const data: WindowLeaderboardEntity[] = useMemo(() => {
|
||||
if (hideCurrentUserRanking || !reply?.user_stats_window) {
|
||||
} = useBoardPagination({ rowPerPage, data: jsonExpandRowModel.toExpandable(reply?.leaderboard || []), limit });
|
||||
const data = useMemo(() => {
|
||||
if (hideCurrentUserRanking || !reply?.user_stats_window || reply.user_stats_window.length === 0) {
|
||||
return paginatedData;
|
||||
}
|
||||
const userStatsWindow: WindowLeaderboardEntity[] = reply.user_stats_window;
|
||||
const userStatsWindow: WindowLeaderboardEntity[] = jsonExpandRowModel.toExpandable(reply.user_stats_window);
|
||||
const userStats = userStatsWindow.find((stats) => stats.highlighted);
|
||||
if (userStats && userStats.rank > end) {
|
||||
paginatedData.push(
|
||||
{ isSpaceRow: true } as WindowLeaderboardEntity,
|
||||
...reply.user_stats_window.filter(
|
||||
...userStatsWindow.filter(
|
||||
(stats) => paginatedData.findIndex((leaderBoardEntity) => leaderBoardEntity.user_id === stats.user_id) === -1
|
||||
) // filter to avoid duplicated row
|
||||
);
|
||||
@@ -116,6 +124,7 @@ export const LeaderboardTable = ({
|
||||
columns={columns}
|
||||
caption={lastUpdated}
|
||||
rowProps={rowProps}
|
||||
getSubRows={jsonExpandRowModel.getSubRows}
|
||||
{...pagnationProps}
|
||||
></DataTable>
|
||||
);
|
||||
|
||||
@@ -1,26 +1,42 @@
|
||||
import { Box, CircularProgress, Flex, Link } from "@chakra-ui/react";
|
||||
import { Box, CircularProgress, Flex, IconButton, Link, Tooltip } from "@chakra-ui/react";
|
||||
import { createColumnHelper } from "@tanstack/react-table";
|
||||
import { ThumbsDown, ThumbsUp } from "lucide-react";
|
||||
import { Mail, ThumbsDown, ThumbsUp, User } from "lucide-react";
|
||||
import NextLink from "next/link";
|
||||
import { FetchTrollBoardResponse, TrollboardEntity, TrollboardTimeFrame } from "src/types/Trollboard";
|
||||
|
||||
import { DataTable } from "../DataTable";
|
||||
import { DataTable, DataTableColumnDef } from "../DataTable/DataTable";
|
||||
import { createJsonExpandRowModel } from "../DataTable/jsonExpandRowModel";
|
||||
import { Discord } from "../Icons/Discord";
|
||||
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 jsonExpandRowModel = createJsonExpandRowModel<TrollboardEntity>();
|
||||
|
||||
const columns = [
|
||||
columnHelper.accessor("rank", {}),
|
||||
const columns: DataTableColumnDef<TrollboardEntity>[] = [
|
||||
{
|
||||
...columnHelper.accessor("rank", {
|
||||
cell: jsonExpandRowModel.renderCell,
|
||||
}),
|
||||
span: jsonExpandRowModel.span,
|
||||
},
|
||||
columnHelper.accessor("display_name", {
|
||||
header: "Display name",
|
||||
cell: ({ getValue, row }) => (
|
||||
<Link as={NextLink} href={`/admin/manage_user/${row.original.user_id}`}>
|
||||
{getValue()}
|
||||
</Link>
|
||||
),
|
||||
cell: ({ getValue, row }) => {
|
||||
const isEmail = row.original.auth_method === "local";
|
||||
return (
|
||||
<Flex gap="2" alignItems="center">
|
||||
<Link as={NextLink} href={`/admin/manage_user/${row.original.user_id}`}>
|
||||
{getValue()}
|
||||
</Link>
|
||||
<Tooltip label={`This user signin with ${isEmail ? "email" : "discord"}`}>
|
||||
{isEmail ? <Mail size="20"></Mail> : <Discord size="20"></Discord>}
|
||||
</Tooltip>
|
||||
</Flex>
|
||||
);
|
||||
},
|
||||
}),
|
||||
columnHelper.accessor("troll_score", {
|
||||
header: "Troll score",
|
||||
@@ -45,36 +61,19 @@ const columns = [
|
||||
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()),
|
||||
cell: ({ getValue }) => toPercentage(getValue() || 0),
|
||||
}),
|
||||
columnHelper.accessor((row) => row.user_id, {
|
||||
header: "Actions",
|
||||
cell: ({ row }) => (
|
||||
<IconButton
|
||||
as={NextLink}
|
||||
href={`/admin/manage_user/${row.original.user_id}`}
|
||||
aria-label={"View user"}
|
||||
icon={<User></User>}
|
||||
></IconButton>
|
||||
),
|
||||
}),
|
||||
];
|
||||
|
||||
@@ -94,7 +93,11 @@ export const TrollboardTable = ({
|
||||
lastUpdated,
|
||||
} = useFetchBoard<FetchTrollBoardResponse>(`/api/admin/trollboard?time_frame=${timeFrame}&limit=${limit}`);
|
||||
|
||||
const { data, ...paginationProps } = useBoardPagination({ rowPerPage, data: trollboardRes?.trollboard, limit });
|
||||
const { data, ...paginationProps } = useBoardPagination<TrollboardEntity>({
|
||||
rowPerPage,
|
||||
data: jsonExpandRowModel.toExpandable(trollboardRes?.trollboard),
|
||||
limit,
|
||||
});
|
||||
const rowProps = useBoardRowProps<TrollboardEntity>();
|
||||
if (isLoading) {
|
||||
return <CircularProgress isIndeterminate></CircularProgress>;
|
||||
@@ -112,11 +115,12 @@ export const TrollboardTable = ({
|
||||
},
|
||||
}}
|
||||
>
|
||||
<DataTable<TrollboardEntity>
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
caption={lastUpdated}
|
||||
rowProps={rowProps}
|
||||
getSubRows={jsonExpandRowModel.getSubRows}
|
||||
{...paginationProps}
|
||||
></DataTable>
|
||||
</Box>
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useColorModeValue, useToken } from "@chakra-ui/react";
|
||||
import { useCallback } from "react";
|
||||
import { colors } from "src/styles/Theme/colors";
|
||||
|
||||
import { DataTableRowPropsCallback } from "../DataTable";
|
||||
import { DataTableRowPropsCallback } from "../DataTable/DataTable";
|
||||
|
||||
export const useBoardRowProps = <T extends { highlighted: boolean }>() => {
|
||||
const borderColor = useToken("colors", useColorModeValue(colors.light.active, colors.dark.active));
|
||||
|
||||
@@ -7,7 +7,7 @@ import { get } from "src/lib/api";
|
||||
import type { FetchUsersResponse, User } from "src/types/Users";
|
||||
import useSWR from "swr";
|
||||
|
||||
import { DataTable, DataTableColumnDef, FilterItem } from "./DataTable";
|
||||
import { DataTable, DataTableColumnDef, FilterItem } from "./DataTable/DataTable";
|
||||
|
||||
interface Pagination {
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@ const Leaderboard = () => {
|
||||
<AdminArea>
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Heading fontSize="2xl" fontWeight="bold" pb="4">
|
||||
{t("leaderboard")}
|
||||
Trollboard
|
||||
</Heading>
|
||||
<Card>
|
||||
<CardBody>
|
||||
|
||||
Reference in New Issue
Block a user