diff --git a/website/src/components/Dashboard/TaskOption.tsx b/website/src/components/Dashboard/TaskOption.tsx index 1b421126..e2bafac3 100644 --- a/website/src/components/Dashboard/TaskOption.tsx +++ b/website/src/components/Dashboard/TaskOption.tsx @@ -1,19 +1,19 @@ import { Box, Flex, GridItem, Heading, SimpleGrid, Text, useColorModeValue } from "@chakra-ui/react"; import Link from "next/link"; -import { TaskTypes } from "../Tasks/TaskTypes"; +import { TaskCategory, TaskCategoryLabels, TaskTypes } from "../Tasks/TaskTypes"; -export const TaskOption = ({ displayTaskCategories }) => { +export const TaskOption = ({ displayTaskCategories }: { displayTaskCategories: TaskCategory[] }) => { const backgroundColor = useColorModeValue("white", "gray.700"); return ( - {displayTaskCategories.map((category, categoryIndex) => ( -
- {category} + {displayTaskCategories.map((category) => ( +
+ {TaskCategoryLabels[category]} - {TaskTypes.filter((task) => task.category === category).map((item, itemIndex) => ( - + {TaskTypes.filter((task) => task.category === category).map((item) => ( + (taskApiEndpoint: string) => { +export const useGenericTaskAPI = (taskType: TaskTypeEnum) => { type ConcreteTaskResponse = TaskResponse; const [tasks, setTasks] = useState([]); - const { isLoading, mutate, error } = useSWRImmutable("/api/new_task/" + taskApiEndpoint, get, { + const { isLoading, mutate, error } = useSWRImmutable("/api/new_task/" + taskType, get, { onSuccess: (data) => setTasks([data]), revalidateOnMount: true, dedupingInterval: 500, }); const { trigger } = useSWRMutation("/api/update_task", post, { - onSuccess: async (response) => { - const newTask: ConcreteTaskResponse = response; + onSuccess: async (newTask: ConcreteTaskResponse) => { setTasks((oldTasks) => [...oldTasks, newTask]); mutate(); }, diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts index de58ae71..b1639462 100644 --- a/website/src/lib/oasst_api_client.ts +++ b/website/src/lib/oasst_api_client.ts @@ -1,5 +1,6 @@ import type { Message } from "src/types/Conversation"; import { LeaderboardReply, LeaderboardTimeFrame } from "src/types/Leaderboard"; +import type { AvailableTasks } from "src/types/Task"; import type { BackendUser, BackendUserCore } from "src/types/Users"; export class OasstError { @@ -205,6 +206,13 @@ export class OasstApiClient { async fetch_leaderboard(time_frame: LeaderboardTimeFrame): Promise { return this.get(`/api/v1/leaderboards/${time_frame}`); } + + /** + * Returns the counts of all tasks (some might be zero) + */ + async fetch_available_tasks(user: BackendUserCore): Promise { + return this.post(`/api/v1/tasks/availability`, user); + } } const oasstApiClient = new OasstApiClient(process.env.FASTAPI_URL, process.env.FASTAPI_KEY); diff --git a/website/src/pages/api/available_tasks.ts b/website/src/pages/api/available_tasks.ts new file mode 100644 index 00000000..36265d25 --- /dev/null +++ b/website/src/pages/api/available_tasks.ts @@ -0,0 +1,11 @@ +import { withoutRole } from "src/lib/auth"; +import { oasstApiClient } from "src/lib/oasst_api_client"; +import { getBackendUserCore } from "src/lib/users"; + +const handler = withoutRole("banned", async (req, res, token) => { + const user = await getBackendUserCore(token.sub); + const availableTasks = await oasstApiClient.fetch_available_tasks(user); + res.status(200).json(availableTasks); +}); + +export default handler; diff --git a/website/src/pages/dashboard.tsx b/website/src/pages/dashboard.tsx index 3d6beb8b..e0b8bba4 100644 --- a/website/src/pages/dashboard.tsx +++ b/website/src/pages/dashboard.tsx @@ -1,11 +1,20 @@ import { Flex } from "@chakra-ui/react"; import Head from "next/head"; +import { useMemo } from "react"; import { LeaderboardTable, 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"; +import type { AvailableTasks, TaskType } from "src/types/Task"; export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props"; +import useSWRImmutable from "swr/immutable"; const Dashboard = () => { + const { data } = useSWRImmutable("/api/available_tasks", get); + + // TODO: show only these tasks: + const availableTasks = useMemo(() => filterAvailableTasks(data ?? {}), [data]); + return ( <> @@ -14,13 +23,19 @@ const Dashboard = () => { - + ); }; -Dashboard.getLayout = (page) => getDashboardLayout(page); +Dashboard.getLayout = getDashboardLayout; export default Dashboard; + +const filterAvailableTasks = (availableTasks: Partial) => + Object.entries(availableTasks) + .filter(([_, count]) => count > 0) + .sort((a, b) => b[1] - a[1]) + .map(([taskType]) => taskType) as TaskType[]; diff --git a/website/src/pages/tasks/random.tsx b/website/src/pages/tasks/random.tsx index d2e850f5..be1809c3 100644 --- a/website/src/pages/tasks/random.tsx +++ b/website/src/pages/tasks/random.tsx @@ -4,9 +4,10 @@ import { getDashboardLayout } from "src/components/Layout"; import { LoadingScreen } from "src/components/Loading/LoadingScreen"; import { Task } from "src/components/Tasks/Task"; import { useGenericTaskAPI } from "src/hooks/tasks/useGenericTaskAPI"; +import { TaskType } from "src/types/Task"; const RandomTask = () => { - const { tasks, isLoading, trigger, reset } = useGenericTaskAPI("random"); + const { tasks, isLoading, trigger, reset } = useGenericTaskAPI(TaskType.random); if (isLoading) { return ; diff --git a/website/src/types/Task.ts b/website/src/types/Task.ts index d58f892c..8e5ada44 100644 --- a/website/src/types/Task.ts +++ b/website/src/types/Task.ts @@ -10,6 +10,8 @@ export const enum TaskType { label_initial_prompt = "label_initial_prompt", label_prompter_reply = "label_prompter_reply", label_assistant_reply = "label_assistant_reply", + + random = "random", } // we need to reconsider how to handle task content types @@ -32,3 +34,5 @@ export interface TaskResponse { userId: string; task: Task; } + +export type AvailableTasks = { [taskType in TaskType]: number };