diff --git a/website/src/components/Dashboard/LeaderboardTable.tsx b/website/src/components/Dashboard/LeaderboardTable.tsx index cd7d22d4..b6048f3f 100644 --- a/website/src/components/Dashboard/LeaderboardTable.tsx +++ b/website/src/components/Dashboard/LeaderboardTable.tsx @@ -1,55 +1,12 @@ -import { Badge, Box, Image, Link, Stack, StackDivider, Text, useColorModeValue } from "@chakra-ui/react"; +import { Box, Link, Stack, StackDivider, Text, useColorModeValue } from "@chakra-ui/react"; import NextLink from "next/link"; +import { get } from "src/lib/api"; +import useSWR from "swr"; export function LeaderboardTable() { const backgroundColor = useColorModeValue("white", "gray.700"); const accentColor = useColorModeValue("gray.200", "gray.900"); - - //need to add streak info to chart - - const leaderInfo = [ - { - name: "fozziethebeat#6690", - image: "/images/temp-avatars/av1.jpg", - score: "5,208", - arrowDir: "increase", - streak: false, - streakCount: "5-Day Streak", - }, - { - name: "k_nearest_neighbor#8579", - image: "/images/temp-avatars/av2.jpg", - score: "5,164", - arrowDir: "decrease", - streak: false, - streakCount: "", - }, - { - name: "andreaskoepf#2266", - image: "/images/temp-avatars/av3.jpg", - score: "5,120", - arrowDir: "", - streak: false, - streakCount: "2-Day Streak", - }, - { - name: "AbdBarho#1684", - image: "/images/temp-avatars/av4.jpg", - score: "4,260", - arrowDir: "", - streak: false, - streakCount: "", - }, - { - name: "zu#9016", - image: "/images/temp-avatars/av5.jpg", - score: "3,608", - arrowDir: "", - streak: false, - streakCount: "", - }, - ]; - + const { data: leaderboardEntries } = useSWR("/api/leaderboard", get); return (
@@ -75,15 +32,19 @@ export function LeaderboardTable() {

Score

- {leaderInfo.map((item, itemIndex) => ( -
+ {leaderboardEntries?.map(({ display_name, score }, idx) => ( +
+ {/* Profile Picture -

{item.name}

+ */} +

{display_name}

+ {/* {item.streakCount} + */}
-

{item.score}

+

{score}

))} diff --git a/website/src/components/EmptyState.tsx b/website/src/components/EmptyState.tsx index aa50bd9f..8d82163c 100644 --- a/website/src/components/EmptyState.tsx +++ b/website/src/components/EmptyState.tsx @@ -1,5 +1,6 @@ -import { Center, Text, useColorMode, useColorModeValue } from "@chakra-ui/react"; -import { FiFileText } from "react-icons/fi"; +import { Box, Link, Text, useColorModeValue } from "@chakra-ui/react"; +import { useRouter } from "next/router"; +import { FiAlertTriangle } from "react-icons/fi"; import { IconType } from "react-icons/lib"; type EmptyStateProps = { @@ -8,25 +9,26 @@ type EmptyStateProps = { }; export const EmptyState = (props: EmptyStateProps) => { - const { colorMode } = useColorMode(); - const mainBgClasses = colorMode === "light" ? "bg-slate-300 text-gray-900" : "bg-slate-900 text-white"; - - const widgetClasses = useColorModeValue("border-gray-700 text-gray-700", "border-gray-300 text-gray-300"); + const backgroundColor = useColorModeValue("white", "gray.800"); + const router = useRouter(); return ( -
-
-
- - - {props.text} - -
-
-
+ + + + {props.text} + router.back()} color="blue.500" textUnderlineOffset="3px"> + Click here to go back + + + ); }; export const TaskEmptyState = () => { - return ; + return ; +}; + +export const PageEmptyState = () => { + return ; }; diff --git a/website/src/components/FlaggableElement.tsx b/website/src/components/FlaggableElement.tsx index f2f3242b..f98ca171 100644 --- a/website/src/components/FlaggableElement.tsx +++ b/website/src/components/FlaggableElement.tsx @@ -1,4 +1,5 @@ import { + Box, Button, Checkbox, Flex, @@ -21,8 +22,9 @@ import { useColorModeValue, useId, } from "@chakra-ui/react"; -import { FlagIcon, QuestionMarkCircleIcon } from "@heroicons/react/20/solid"; +import { QuestionMarkCircleIcon } from "@heroicons/react/20/solid"; import { useEffect, useReducer } from "react"; +import { FiAlertCircle } from "react-icons/fi"; import { get, post } from "src/lib/api"; import { Message } from "src/types/Conversation"; import { colors } from "styles/Theme/colors"; @@ -98,7 +100,6 @@ export const FlaggableElement = (props: FlaggableElementProps) => { { label_values: [], submittable: false } ); const [isEditing, setIsEditing] = useBoolean(); - const backgroundColor = useColorModeValue("gray.200", "gray.700"); const { data, isLoading } = useSWR("/api/valid_labels", get); useEffect(() => { @@ -145,20 +146,20 @@ export const FlaggableElement = (props: FlaggableElementProps) => { isLazy lazyBehavior="keepMounted" > - + {props.children} - +
- + +
- +
@@ -224,7 +225,7 @@ export function FlagCheckbox(props: FlagCheckboxProps): JSX.Element { : `text-${colors.dark.text} hover:text-blue-400 float-left`; return ( - + ; @@ -29,6 +37,8 @@ export function UserMenu() { desc: "Account Settings", icon: FiSettings, }, + ]; + const helpOptions = [ { name: "Report a Bug", href: "https://github.com/LAION-AI/Open-Assistant/issues/new/choose", @@ -47,87 +57,53 @@ export function UserMenu() { } return ( - - {({ open }) => ( - <> - - - Profile Picture -

- {session.user.name || session.user.email} -

-
-
- - {open && ( - - - - - {accountOptions.map((item) => ( - -
-
-
- {item.name} -
- - ))} - signOut({ callbackUrl: "/" })} - > -
- -
-
- Sign Out -
- -
-
-
-
- )} -
- - )} -
+ <> + + + + + + {session.user.name || session.user.email} + + + + + + {session.user.name} + + 3,200 + + + + + {accountOptions.map((item) => ( + + + + + ))} + + + + {helpOptions.map((item) => ( + + + + + ))} + + + signOut({ callbackUrl: "/" })}> + + + + ); } } diff --git a/website/src/components/LeaderboardGridCell/LeaderboardGridCell.tsx b/website/src/components/LeaderboardGridCell/LeaderboardGridCell.tsx new file mode 100644 index 00000000..51dd877e --- /dev/null +++ b/website/src/components/LeaderboardGridCell/LeaderboardGridCell.tsx @@ -0,0 +1,97 @@ +import { Avatar, Box, Grid, GridItem, Text, useColorModeValue } from "@chakra-ui/react"; +import { FiChevronDown } from "react-icons/fi"; +import { get } from "src/lib/api"; +import useSWR from "swr"; + +/** + * Presents a grid of leaderboard entries with more detailed information. + */ +const LeaderboardGridCell = () => { + const { data: leaderboardEntries } = useSWR("/api/leaderboard", get); + const backgroundColor = useColorModeValue("white", "gray.800"); + const columns = `repeat(${FILTER.length}, 1fr)`; + + return ( + <> + + + {FILTER.map(({ title, GridItemProps }, index) => ( + + + + {title} + + + + + + ))} + + + + {leaderboardEntries?.map(({ display_name, ranking, score }, index) => ( + + + + + {display_name} + + + + + {ranking} + + + + {score} + + {/* + + {item.medal} + + */} + + ))} + + + ); +}; + +/** + * Specifies the table headers in the grid. + */ +const FILTER = [ + { + title: "User", + isActive: false, + GridItemProps: { justifyContent: "start" }, + }, + { + title: "Rank", + isActive: false, + GridItemProps: { justifyContent: "center" }, + }, + { + title: "Score", + isActive: false, + GridItemProps: { justifyContent: "center" }, + }, + /* + { + title: "Medal", + isActive: false, + GridItemProps: { justifyContent: "center" }, + }, + */ +]; + +export { LeaderboardGridCell }; diff --git a/website/src/components/LeaderboardGridCell/index.tsx b/website/src/components/LeaderboardGridCell/index.tsx new file mode 100644 index 00000000..c4657eb6 --- /dev/null +++ b/website/src/components/LeaderboardGridCell/index.tsx @@ -0,0 +1 @@ +export * from "./LeaderboardGridCell"; diff --git a/website/src/components/Loading/LoadingScreen.jsx b/website/src/components/Loading/LoadingScreen.jsx index 3aad717f..645544e6 100644 --- a/website/src/components/Loading/LoadingScreen.jsx +++ b/website/src/components/Loading/LoadingScreen.jsx @@ -1,14 +1,12 @@ import { Box, Center, Progress, Text, useColorModeValue } from "@chakra-ui/react"; export const LoadingScreen = ({ text = "Loading..." } = {}) => { - const mainBgClasses = useColorModeValue("bg-slate-300 text-gray-900", "bg-slate-900 text-white"); - return ( - + {text && ( -
- {text} +
+ {text}
)} diff --git a/website/src/components/Messages/MessageTable.tsx b/website/src/components/Messages/MessageTable.tsx index 4bb67128..d82583f6 100644 --- a/website/src/components/Messages/MessageTable.tsx +++ b/website/src/components/Messages/MessageTable.tsx @@ -1,4 +1,4 @@ -import { Stack, StackDivider } from "@chakra-ui/react"; +import { Stack } from "@chakra-ui/react"; import { MessageTableEntry } from "src/components/Messages/MessageTableEntry"; import { Message } from "src/types/Conversation"; @@ -9,7 +9,7 @@ interface MessageTableProps { export function MessageTable({ messages, enableLink }: MessageTableProps) { return ( - } spacing="4"> + {messages.map((item) => ( ))} diff --git a/website/src/components/Messages/MessageTableEntry.tsx b/website/src/components/Messages/MessageTableEntry.tsx index d7b8b08c..64bb332f 100644 --- a/website/src/components/Messages/MessageTableEntry.tsx +++ b/website/src/components/Messages/MessageTableEntry.tsx @@ -11,30 +11,44 @@ interface MessageTableEntryProps { export function MessageTableEntry(props: MessageTableEntryProps) { const { item } = props; - const backgroundColor = useColorModeValue("gray.50", "gray.800"); + const backgroundColor = useColorModeValue("gray.100", "gray.700"); + const backgroundColor2 = useColorModeValue("#DFE8F1", "#42536B"); + + const avatarColor = useColorModeValue("white", "black"); + const borderColor = useColorModeValue("blackAlpha.200", "whiteAlpha.200"); + return ( -
- - + + + - - {props.enabled ? ( + + {props.enabled ? ( + - + {item.text} - ) : ( - + + ) : ( + + {item.text} - )} - - -
+ + )} + + ); } diff --git a/website/src/components/Messages/MessageWithChildren.tsx b/website/src/components/Messages/MessageWithChildren.tsx index 39b6bbbd..f4776773 100644 --- a/website/src/components/Messages/MessageWithChildren.tsx +++ b/website/src/components/Messages/MessageWithChildren.tsx @@ -26,8 +26,8 @@ interface MessageWithChildrenProps { } export function MessageWithChildren(props: MessageWithChildrenProps) { - const backgroundColor = useColorModeValue("white", "gray.700"); - const childBackgroundColor = useColorModeValue("gray.200", "gray.800"); + const backgroundColor = useColorModeValue("white", "gray.800"); + const childBackgroundColor = useColorModeValue("gray.200", "gray.700"); const { id, depth, maxDepth, isOnlyChild = true } = props; @@ -93,11 +93,21 @@ export function MessageWithChildren(props: MessageWithChildrenProps) { <> {isFirstOrOnly ? "Children" : "Ancestor"} - {children.map((item, idx) => ( - - - - ))} + + {children.map((item, idx) => ( + + + + ))} + ) diff --git a/website/src/components/RankItem.tsx b/website/src/components/RankItem.tsx deleted file mode 100644 index daed0576..00000000 --- a/website/src/components/RankItem.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Avatar, Box, GridItem, Text } from "@chakra-ui/react"; - -const RankItem = () => { - const leaderInfo = [ - { - username: "fozziethebeat", - rank: 1, - score: 530, - medal: "\uD83E\uDD47", - }, - { - username: "k_nearest", - rank: 2, - score: 420, - medal: "\uD83E\uDD48", - }, - { - username: "zu", - rank: 3, - score: 160, - medal: "\uD83E\uDD49", - }, - { - username: "Abd", - rank: 4, - score: 140, - medal: "", - }, - ]; - - return ( - <> - {leaderInfo.map((item, index) => ( - - - - - {item.username} - - - - - {item.rank} - - - - {item.score} - - - {item.medal} - - - ))} - - ); -}; - -export default RankItem; diff --git a/website/src/components/SideMenuLayout.tsx b/website/src/components/SideMenuLayout.tsx index d2220f36..a768bc85 100644 --- a/website/src/components/SideMenuLayout.tsx +++ b/website/src/components/SideMenuLayout.tsx @@ -12,11 +12,11 @@ export const SideMenuLayout = (props: SideMenuLayoutProps) => { return ( - - + + - {props.children} + {props.children} ); diff --git a/website/src/components/Survey/SurveyCard.tsx b/website/src/components/Survey/SurveyCard.tsx index 1126fa0b..5a78ce2b 100644 --- a/website/src/components/Survey/SurveyCard.tsx +++ b/website/src/components/Survey/SurveyCard.tsx @@ -6,13 +6,13 @@ interface SurveyCardProps { } export const SurveyCard = (props: SurveyCardProps) => { - const backgroundColor = useColorModeValue("white", "gray.800"); + const backgroundColor = useColorModeValue("white", "gray.700"); const BoxClasses: BoxProps = { - p: "6", gap: "2", borderRadius: "xl", shadow: "base", + className: "p-4 sm:p-6", }; return ( diff --git a/website/src/components/Tasks/CreateTask.tsx b/website/src/components/Tasks/CreateTask.tsx index 27273acf..07d4e64d 100644 --- a/website/src/components/Tasks/CreateTask.tsx +++ b/website/src/components/Tasks/CreateTask.tsx @@ -12,7 +12,7 @@ export const CreateTask = ({ isDisabled, onReplyChanged, }: TaskSurveyProps<{ text: string }>) => { - const cardColor = useColorModeValue("gray.100", "gray.700"); + const cardColor = useColorModeValue("gray.50", "gray.900"); const titleColor = useColorModeValue("gray.800", "gray.300"); const labelColor = useColorModeValue("gray.600", "gray.400"); @@ -42,7 +42,7 @@ export const CreateTask = ({
{task.conversation ? ( - + ) : null} diff --git a/website/src/components/Tasks/EvaluateTask.tsx b/website/src/components/Tasks/EvaluateTask.tsx index 7e99f490..5c1c77ad 100644 --- a/website/src/components/Tasks/EvaluateTask.tsx +++ b/website/src/components/Tasks/EvaluateTask.tsx @@ -22,13 +22,9 @@ export const EvaluateTask = ({ } useEffect(() => { - const conversationMsgs = task.conversation ? task.conversation.messages : []; - const defaultRanking = conversationMsgs.map((message, index) => index); - onReplyChanged({ - content: { ranking: defaultRanking }, - state: "DEFAULT", - }); - }, [task.conversation, onReplyChanged]); + const ranking = (task.replies ?? task.prompts).map((_, idx) => idx); + onReplyChanged({ content: { ranking }, state: "DEFAULT" }); + }, [task, onReplyChanged]); const onRank = (newRanking: number[]) => { onReplyChanged({ content: { ranking: newRanking }, state: "VALID" }); diff --git a/website/src/components/Tasks/LabelTask.tsx b/website/src/components/Tasks/LabelTask.tsx index 695ede2c..aac0f416 100644 --- a/website/src/components/Tasks/LabelTask.tsx +++ b/website/src/components/Tasks/LabelTask.tsx @@ -3,11 +3,11 @@ import { Text, useColorModeValue } from "@chakra-ui/react"; import { useEffect, useState } from "react"; import { MessageView } from "src/components/Messages"; import { MessageTable } from "src/components/Messages/MessageTable"; +import { LabelRadioGroup } from "src/components/Survey/LabelRadioGroup"; +import { LabelSliderGroup } from "src/components/Survey/LabelSliderGroup"; import { TwoColumnsWithCards } from "src/components/Survey/TwoColumnsWithCards"; import { TaskSurveyProps } from "src/components/Tasks/Task"; import { TaskType } from "src/types/Task"; -import { LabelSliderGroup } from "src/components/Survey/LabelSliderGroup"; -import { LabelRadioGroup } from "src/components/Survey/LabelRadioGroup"; export const LabelTask = ({ task, diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts index 95e2fb58..a26b421d 100644 --- a/website/src/lib/oasst_api_client.ts +++ b/website/src/lib/oasst_api_client.ts @@ -207,11 +207,19 @@ export class OasstApiClient { return this.put(`/api/v1/users/users/${user_id}?enabled=${is_enabled}¬es=${notes}`); } - //Fetch valid labels. This is called every task. though the call may be redundant - //keeping this for future where the valid labels may change per task - async fetch_valid_text(): Promise { + /** + * Returns the valid labels for messages. + */ + async fetch_valid_text(): Promise { return this.get(`/api/v1/text_labels/valid_labels`); } + + /** + * Returns the current leaderboard ranking. + */ + async fetch_leaderboard(): Promise { + return this.get(`/api/v1/experimental/leaderboards/create/assistant`); + } } const oasstApiClient = new OasstApiClient(process.env.FASTAPI_URL, process.env.FASTAPI_KEY); diff --git a/website/src/pages/404.tsx b/website/src/pages/404.tsx index aae1db0f..c3d01510 100644 --- a/website/src/pages/404.tsx +++ b/website/src/pages/404.tsx @@ -1,32 +1,47 @@ -import { Button, Link, Stack } from "@chakra-ui/react"; +import { Box, Button, Center, Link, Text, useColorModeValue } from "@chakra-ui/react"; import Head from "next/head"; -import NextLink from "next/link"; +import { useRouter } from "next/router"; import { FiAlertTriangle } from "react-icons/fi"; +import { PageEmptyState } from "src/components/EmptyState"; +import { getTransparentHeaderLayout } from "src/components/Layout"; + +function Error() { + const router = useRouter(); + const backgroundColor = useColorModeValue("white", "gray.800"); -export default function Error() { return ( <> 404 - Open Assistant -
- -

Sorry, the page you are looking for does not exist.

-

If you were trying to contribute data but ended up here, please file a bug

- -
-
+
+
); } + +Error.getLayout = getTransparentHeaderLayout; + +export default Error; diff --git a/website/src/pages/api/leaderboard.ts b/website/src/pages/api/leaderboard.ts new file mode 100644 index 00000000..d2a591a8 --- /dev/null +++ b/website/src/pages/api/leaderboard.ts @@ -0,0 +1,18 @@ +import { withoutRole } from "src/lib/auth"; +import { oasstApiClient } from "src/lib/oasst_api_client"; + +/** + * Returns the set of valid labels that can be applied to messages. + */ +const handler = withoutRole("banned", async (req, res) => { + const { leaderboard } = await oasstApiClient.fetch_leaderboard(); + res.status(200).json( + leaderboard.map(({ display_name, ranking, score }) => ({ + display_name, + ranking, + score, + })) + ); +}); + +export default handler; diff --git a/website/src/pages/leaderboard.tsx b/website/src/pages/leaderboard.tsx new file mode 100644 index 00000000..ef2c4529 --- /dev/null +++ b/website/src/pages/leaderboard.tsx @@ -0,0 +1,25 @@ +import { Box, Heading } from "@chakra-ui/react"; +import Head from "next/head"; +import { getDashboardLayout } from "src/components/Layout"; +import { LeaderboardGridCell } from "src/components/LeaderboardGridCell"; + +const Leaderboard = () => { + return ( + <> + + Leaderboard - Open Assistant + + + + + Leaderboard + + + + + ); +}; + +Leaderboard.getLayout = getDashboardLayout; + +export default Leaderboard; diff --git a/website/src/pages/leaderboard/index.tsx b/website/src/pages/leaderboard/index.tsx deleted file mode 100644 index a350e092..00000000 --- a/website/src/pages/leaderboard/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { Box, Grid, GridItem, GridItemProps, Heading, Text, useColorModeValue } from "@chakra-ui/react"; -import Head from "next/head"; -import { FiChevronDown } from "react-icons/fi"; -import { getDashboardLayout } from "src/components/Layout"; -import RankItem from "src/components/RankItem"; - -const Leaderboard = () => { - const backgroundColor = useColorModeValue("white", "gray.800"); - - const GridProps: GridItemProps = { - justifyContent: "start", - }; - const filter = [ - { - title: "User", - isActive: false, - GridItemProps: { ...GridProps, justifyContent: "start" }, - }, - { - title: "Rank", - isActive: false, - GridItemProps: { ...GridProps, justifyContent: "center" }, - }, - { - title: "Score", - isActive: false, - GridItemProps: { ...GridProps, justifyContent: "center" }, - }, - { - title: "Medal", - isActive: false, - GridItemProps: { ...GridProps, justifyContent: "center" }, - }, - ]; - - return ( - <> - - Leaderboard - Open Assistant - - - - - Leaderboard - - - - {filter.map((item, index) => ( - - - - {item.title} - - - - - - ))} - - - - - - - - ); -}; - -Leaderboard.getLayout = getDashboardLayout; - -export default Leaderboard; diff --git a/website/src/pages/messages/[id]/index.tsx b/website/src/pages/messages/[id]/index.tsx index c1712b6d..7d47873f 100644 --- a/website/src/pages/messages/[id]/index.tsx +++ b/website/src/pages/messages/[id]/index.tsx @@ -9,7 +9,7 @@ import { get } from "src/lib/api"; import useSWR from "swr"; const MessageDetail = ({ id }) => { - const backgroundColor = useColorModeValue("white", "gray.700"); + const backgroundColor = useColorModeValue("white", "gray.800"); const [parent, setParent] = useState(null); const { isLoading: isLoadingParent } = useSWR(id ? `/api/messages/${id}/parent` : null, get, { @@ -38,11 +38,10 @@ const MessageDetail = ({ id }) => { {parent && ( <> - + Parent - {" "} diff --git a/website/src/pages/messages/index.tsx b/website/src/pages/messages/index.tsx index 1da42db4..627a8b18 100644 --- a/website/src/pages/messages/index.tsx +++ b/website/src/pages/messages/index.tsx @@ -1,38 +1,16 @@ import { Box, CircularProgress, SimpleGrid, Text, useColorModeValue } from "@chakra-ui/react"; import Head from "next/head"; -import { useEffect, useState } from "react"; import { getDashboardLayout } from "src/components/Layout"; import { MessageTable } from "src/components/Messages/MessageTable"; import { get } from "src/lib/api"; -import { Message } from "src/types/Conversation"; import useSWRImmutable from "swr/immutable"; const MessagesDashboard = () => { - const boxBgColor = useColorModeValue("white", "gray.700"); + const boxBgColor = useColorModeValue("white", "gray.800"); const boxAccentColor = useColorModeValue("gray.200", "gray.900"); - const [messages, setMessages] = useState(null); - const [userMessages, setUserMessages] = useState(null); - - const { isLoading: isLoadingAll, mutate: mutateAll } = useSWRImmutable("/api/messages", get, { - onSuccess: setMessages, - }); - - const { isLoading: isLoadingUser, mutate: mutateUser } = useSWRImmutable(`/api/messages/user`, get, { - onSuccess: setUserMessages, - }); - - const receivedMessages = !isLoadingAll && Array.isArray(messages); - const receivedUserMessages = !isLoadingUser && Array.isArray(userMessages); - - useEffect(() => { - if (!receivedMessages) { - mutateAll(); - } - if (!receivedUserMessages) { - mutateUser(); - } - }, [receivedMessages, mutateAll, receivedUserMessages, mutateUser]); + const { data: messages } = useSWRImmutable("/api/messages", get, { revalidateOnMount: true }); + const { data: userMessages } = useSWRImmutable(`/api/messages/user`, get, { revalidateOnMount: true }); return ( <> @@ -40,24 +18,24 @@ const MessagesDashboard = () => { Messages - Open Assistant - + - Recent messages + Recent Messages - {receivedMessages ? : } + {messages ? : } - Your recent messages + Your Recent Messages { borderRadius="xl" className="p-6 shadow-sm" > - {receivedUserMessages ? ( - - ) : ( - - )} + {userMessages ? : } diff --git a/website/src/types/Leaderboard.ts b/website/src/types/Leaderboard.ts new file mode 100644 index 00000000..ea3c9dae --- /dev/null +++ b/website/src/types/Leaderboard.ts @@ -0,0 +1,5 @@ +export interface LeaderboardEntry { + display_name: string; + ranking: number; + score: number; +}