From 801ad553b846c96843d83c0fcdf3fd4ec5af9753 Mon Sep 17 00:00:00 2001 From: notmd Date: Wed, 18 Jan 2023 15:19:00 +0700 Subject: [PATCH 01/17] Allow to filter `user` by `display_name` --- backend/oasst_backend/user_repository.py | 4 +- website/package-lock.json | 45 +++++++ website/package.json | 1 + website/src/components/DataTable.tsx | 158 +++++++++++++++++++++++ website/src/components/UserTable.tsx | 133 +++++++++++++++++++ website/src/components/UsersCell.tsx | 137 -------------------- website/src/lib/oasst_api_client.ts | 10 ++ website/src/pages/admin/index.tsx | 5 +- website/src/pages/api/admin/users.ts | 13 +- 9 files changed, 361 insertions(+), 145 deletions(-) create mode 100644 website/src/components/DataTable.tsx create mode 100644 website/src/components/UserTable.tsx delete mode 100644 website/src/components/UsersCell.tsx diff --git a/backend/oasst_backend/user_repository.py b/backend/oasst_backend/user_repository.py index 578dc5f1..c244d67f 100644 --- a/backend/oasst_backend/user_repository.py +++ b/backend/oasst_backend/user_repository.py @@ -161,10 +161,10 @@ class UserRepository: users = users.order_by(User.display_name) if gt: - users = users.filter(User.display_name > gt) + users = users.filter(User.id > gt) if lt: - users = users.filter(User.display_name < lt) + users = users.filter(User.id < lt).order_by(None).order_by(User.id.desc()) if limit is not None: users = users.limit(limit) diff --git a/website/package-lock.json b/website/package-lock.json index 1fa3d14d..29cd0326 100644 --- a/website/package-lock.json +++ b/website/package-lock.json @@ -21,6 +21,7 @@ "@next/font": "^13.1.0", "@prisma/client": "^4.7.1", "@tailwindcss/forms": "^0.5.3", + "@tanstack/react-table": "^8.7.6", "autoprefixer": "^10.4.13", "axios": "^1.2.1", "boolean": "^3.2.0", @@ -12294,6 +12295,37 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" } }, + "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==", + "dependencies": { + "@tanstack/table-core": "8.7.6" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "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==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "8.19.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.1.tgz", @@ -46565,6 +46597,19 @@ "mini-svg-data-uri": "^1.2.3" } }, + "@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==", + "requires": { + "@tanstack/table-core": "8.7.6" + } + }, + "@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==" + }, "@testing-library/dom": { "version": "8.19.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-8.19.1.tgz", diff --git a/website/package.json b/website/package.json index 580d0be3..6dcbb26a 100644 --- a/website/package.json +++ b/website/package.json @@ -38,6 +38,7 @@ "@next/font": "^13.1.0", "@prisma/client": "^4.7.1", "@tailwindcss/forms": "^0.5.3", + "@tanstack/react-table": "^8.7.6", "autoprefixer": "^10.4.13", "axios": "^1.2.1", "boolean": "^3.2.0", diff --git a/website/src/components/DataTable.tsx b/website/src/components/DataTable.tsx new file mode 100644 index 00000000..eafca6d1 --- /dev/null +++ b/website/src/components/DataTable.tsx @@ -0,0 +1,158 @@ +import { + Box, + Button, + Card, + CardBody, + Flex, + FormControl, + FormLabel, + Input, + Popover, + PopoverArrow, + PopoverBody, + PopoverCloseButton, + PopoverContent, + PopoverTrigger, + Spacer, + Table, + TableCaption, + TableContainer, + Tbody, + Td, + Th, + Thead, + Tr, + useDisclosure, +} from "@chakra-ui/react"; +import { ColumnDef, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table"; +import { ChangeEvent, ReactNode } from "react"; +import { FaFilter } from "react-icons/fa"; +import { useDebouncedCallback } from "use-debounce"; + +export type DataTableColumnDef = ColumnDef & { + filterable?: boolean; +}; + +// TODO: stricter type +export type FilterItem = { + id: string; + value: string; +}; + +export type DataTableProps = { + data: T[]; + columns: DataTableColumnDef[]; + caption?: string; + filterValues?: FilterItem[]; + onNextClick?: () => void; + onPreviousClick?: () => void; + onFilterChange?: (items: FilterItem[]) => void; +}; + +export const DataTable = ({ + data, + columns, + caption, + filterValues = [], + onNextClick, + onPreviousClick, + onFilterChange, +}: DataTableProps) => { + const { getHeaderGroups, getRowModel } = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + + const handleFilterChange = (value: FilterItem) => { + const idx = filterValues.findIndex((oldValue) => oldValue.id === value.id); + let newValues: FilterItem[] = []; + if (idx === -1) { + newValues = [...filterValues, value]; + } else { + newValues = filterValues.map((oldValue) => (oldValue.id === value.id ? value : oldValue)); + } + onFilterChange(newValues); + }; + return ( + + + + + + + + + + {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())}
+
+
+
+ ); +}; + +const FilterModal = ({ + label, + onChange, + value, +}: { + label: ReactNode; + onChange: (val: string) => void; + value: string; +}) => { + const { isOpen, onOpen, onClose } = useDisclosure(); + + const handleInputChange = useDebouncedCallback((e: ChangeEvent) => { + onChange(e.target.value); + }, 500); + + return ( + + + + + + + + + + {label} + + + + + + ); +}; diff --git a/website/src/components/UserTable.tsx b/website/src/components/UserTable.tsx new file mode 100644 index 00000000..3b63255a --- /dev/null +++ b/website/src/components/UserTable.tsx @@ -0,0 +1,133 @@ +import { IconButton, useToast } from "@chakra-ui/react"; +import { createColumnHelper } from "@tanstack/react-table"; +import Link from "next/link"; +import { memo, useState } from "react"; +import { FaPen } from "react-icons/fa"; +import { get } from "src/lib/api"; +import type { User } from "src/types/Users"; +import useSWR from "swr"; + +import { DataTable, DataTableColumnDef, FilterItem } from "./DataTable"; + +interface Pagination { + /** + * The user's `display_name` used for pagination. + */ + cursor: string; + + /** + * The pagination direction. + */ + direction: "forward" | "back"; +} + +const columnHelper = createColumnHelper(); + +const columns: DataTableColumnDef[] = [ + columnHelper.accessor("user_id", { + header: "ID", + }), + columnHelper.accessor("id", { + header: "Auth ID", + }), + columnHelper.accessor("auth_method", { + header: "Auth Method", + }), + { + ...columnHelper.accessor("display_name", { + header: "Name", + }), + filterable: true, + }, + columnHelper.accessor("role", { + header: "Role", + }), + columnHelper.accessor((user) => user.user_id, { + cell: ({ getValue }) => ( + } + > + ), + header: "Update", + }), +]; + +export const UserTable = memo(function UserTable() { + const toast = useToast(); + const [pagination, setPagination] = useState({ cursor: "", direction: "forward" }); + const [users, setUsers] = useState([]); + const [filterValues, setFilterValues] = useState([]); + // Fetch and save the users. + // This follows useSWR's recommendation for simple pagination: + // https://swr.vercel.app/docs/pagination#when-to-use-useswr + const display_name = filterValues.find((value) => value.id === "display_name")?.value ?? ""; + useSWR( + `/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}&display_name=${display_name}`, + get, + { + onSuccess: (data) => { + // When no more users can be found, trigger a toast to indicate why no + // changes have taken place. We have to maintain a non-empty set of + // users otherwise we can't paginate using a cursor (since we've lost the + // cursor). + if (data.length === 0) { + toast({ + title: "No more users", + status: "warning", + duration: 1000, + isClosable: true, + }); + return; + } + setUsers(data); + }, + } + ); + + const toPreviousPage = () => { + if (users.length >= 0) { + setPagination({ + cursor: users[0].user_id, + direction: "back", + }); + } else { + toast({ + title: "Can not paginate when no users are found", + status: "warning", + duration: 1000, + isClosable: true, + }); + } + }; + + const toNextPage = () => { + if (users.length >= 0) { + setPagination({ + cursor: users[users.length - 1].user_id, + direction: "forward", + }); + } else { + toast({ + title: "Can not paginate when no users are found", + status: "warning", + duration: 1000, + isClosable: true, + }); + } + }; + + return ( + + ); +}); diff --git a/website/src/components/UsersCell.tsx b/website/src/components/UsersCell.tsx deleted file mode 100644 index 99824090..00000000 --- a/website/src/components/UsersCell.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { - Button, - Flex, - Spacer, - Stack, - Table, - TableCaption, - TableContainer, - Tbody, - Td, - Th, - Thead, - Tr, - useToast, -} from "@chakra-ui/react"; -import Link from "next/link"; -import { useState } from "react"; -import { get } from "src/lib/api"; -import type { User } from "src/types/Users"; -import useSWR from "swr"; - -interface Pagination { - /** - * The user's `display_name` used for pagination. - */ - cursor: string; - - /** - * The pagination direction. - */ - direction: "forward" | "back"; -} - -/** - * Fetches users from the users api route and then presents them in a simple Chakra table. - */ -const UsersCell = () => { - const toast = useToast(); - const [pagination, setPagination] = useState({ cursor: "", direction: "forward" }); - const [users, setUsers] = useState([]); - - // Fetch and save the users. - // This follows useSWR's recommendation for simple pagination: - // https://swr.vercel.app/docs/pagination#when-to-use-useswr - useSWR(`/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}`, get, { - onSuccess: (data) => { - // When no more users can be found, trigger a toast to indicate why no - // changes have taken place. We have to maintain a non-empty set of - // users otherwise we can't paginate using a cursor (since we've lost the - // cursor). - if (data.length === 0) { - toast({ - title: "No more users", - status: "warning", - duration: 1000, - isClosable: true, - }); - return; - } - setUsers(data); - }, - }); - - const toPreviousPage = () => { - if (users.length >= 0) { - setPagination({ - cursor: users[0].display_name, - direction: "back", - }); - } else { - toast({ - title: "Can not paginate when no users are found", - status: "warning", - duration: 1000, - isClosable: true, - }); - } - }; - - const toNextPage = () => { - if (users.length >= 0) { - setPagination({ - cursor: users[users.length - 1].display_name, - direction: "forward", - }); - } else { - toast({ - title: "Can not paginate when no users are found", - status: "warning", - duration: 1000, - isClosable: true, - }); - } - }; - - // Present users in a naive table. - return ( - - - - - - - - - Users - - - - - - - - - - - - {users.map(({ id, user_id, auth_method, display_name, role }) => ( - - - - - - - - - ))} - -
IdAuth IdAuth MethodNameRoleUpdate
{user_id}{id}{auth_method}{display_name}{role} - Manage -
-
-
- ); -}; - -export default UsersCell; diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts index fb11adec..866b2907 100644 --- a/website/src/lib/oasst_api_client.ts +++ b/website/src/lib/oasst_api_client.ts @@ -187,6 +187,16 @@ export class OasstApiClient { return this.get(url); } + async fetch_user_by_display_name(name: string): Promise { + const params = new URLSearchParams({ + search_text: name, + }); + + const endpoint = `/api/v1/frontend_users/by_display_name`; + + return this.get(`${endpoint}?${params.toString()}`); + } + /** * Returns the `Message`s associated with `user_id` in the backend. */ diff --git a/website/src/pages/admin/index.tsx b/website/src/pages/admin/index.tsx index 9cbea222..397230bd 100644 --- a/website/src/pages/admin/index.tsx +++ b/website/src/pages/admin/index.tsx @@ -3,7 +3,7 @@ import { useRouter } from "next/router"; import { useSession } from "next-auth/react"; import { useEffect } from "react"; import { getAdminLayout } from "src/components/Layout"; -import UsersCell from "src/components/UsersCell"; +import { UserTable } from "src/components/UserTable"; /** * Provides the admin index page that will display a list of users and give @@ -27,7 +27,6 @@ const AdminIndex = () => { } router.push("/"); }, [router, session, status]); - return ( <> @@ -37,7 +36,7 @@ const AdminIndex = () => { content="Conversational AI for everyone. An open source project to create a chat enabled GPT LLM run by LAION and contributors around the world." /> -
{status === "loading" ? "loading..." : }
+
{status === "loading" ? "loading..." : }
); }; diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index e600650d..5cc41354 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -1,11 +1,12 @@ import { withRole } from "src/lib/auth"; import { oasstApiClient } from "src/lib/oasst_api_client"; import prisma from "src/lib/prismadb"; +import { BackendUser } from "src/types/Users"; /** * The number of users to fetch in a single request. Could later be a query parameter. */ -const PAGE_SIZE = 20; +const PAGE_SIZE = 1; /** * Returns a list of user results from the database when the requesting user is @@ -17,10 +18,16 @@ const PAGE_SIZE = 20; * direction. */ const handler = withRole("admin", async (req, res) => { - const { cursor, direction } = req.query; + const { cursor, direction, display_name = "" } = req.query; // First, get all the users according to the backend. - const all_users = await oasstApiClient.fetch_users(PAGE_SIZE, cursor as string, direction === "forward"); + let all_users: BackendUser[] = []; + + if (typeof display_name === "string" && display_name) { + all_users = await oasstApiClient.fetch_user_by_display_name(display_name); + } else { + all_users = await oasstApiClient.fetch_users(PAGE_SIZE, cursor as string, direction === "forward"); + } // Next, get all the users stored in the web's auth database to fetch their role. const local_user_ids = all_users.map(({ id }) => id); From 8eff6932d6867e85b53de314e744f46d3f36953c Mon Sep 17 00:00:00 2001 From: notmd Date: Wed, 18 Jan 2023 15:40:20 +0700 Subject: [PATCH 02/17] switch PAGE_SIZE back to 20 --- website/src/pages/api/admin/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index 5cc41354..52921213 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -6,7 +6,7 @@ import { BackendUser } from "src/types/Users"; /** * The number of users to fetch in a single request. Could later be a query parameter. */ -const PAGE_SIZE = 1; +const PAGE_SIZE = 20; /** * Returns a list of user results from the database when the requesting user is From 622a4768f63415ca2b5ff1b9efa31f7e6550e2d4 Mon Sep 17 00:00:00 2001 From: notmd Date: Wed, 18 Jan 2023 16:07:54 +0700 Subject: [PATCH 03/17] fix default column --- backend/oasst_backend/user_repository.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/oasst_backend/user_repository.py b/backend/oasst_backend/user_repository.py index c244d67f..7c46a026 100644 --- a/backend/oasst_backend/user_repository.py +++ b/backend/oasst_backend/user_repository.py @@ -158,7 +158,7 @@ class UserRepository: if auth_method: users = users.filter(User.auth_method == auth_method) - users = users.order_by(User.display_name) + users = users.order_by(User.id) if gt: users = users.filter(User.id > gt) From 7eb5023e8222f0977ad0d56f45107342a9f3df14 Mon Sep 17 00:00:00 2001 From: notmd Date: Sat, 21 Jan 2023 14:34:22 +0700 Subject: [PATCH 04/17] use cursor endpoint --- website/src/components/UserTable.tsx | 63 +++++++++++++++------------- website/src/lib/oasst_api_client.ts | 51 ++++++++++++++++------ website/src/pages/api/admin/users.ts | 26 ++++++------ 3 files changed, 86 insertions(+), 54 deletions(-) diff --git a/website/src/components/UserTable.tsx b/website/src/components/UserTable.tsx index 3b63255a..68285bfa 100644 --- a/website/src/components/UserTable.tsx +++ b/website/src/components/UserTable.tsx @@ -4,6 +4,7 @@ import Link from "next/link"; import { memo, useState } from "react"; import { FaPen } from "react-icons/fa"; import { get } from "src/lib/api"; +import { FetchUsersResponse } from "src/lib/oasst_api_client"; import type { User } from "src/types/Users"; import useSWR from "swr"; @@ -58,39 +59,43 @@ const columns: DataTableColumnDef[] = [ export const UserTable = memo(function UserTable() { const toast = useToast(); const [pagination, setPagination] = useState({ cursor: "", direction: "forward" }); - const [users, setUsers] = useState([]); + const [response, setResponse] = useState, "sort_key" | "order">>({ + items: [], + }); const [filterValues, setFilterValues] = useState([]); + const handleFilterValuesChange = (values: FilterItem[]) => { + setFilterValues(values); + setPagination((old) => ({ ...old, cursor: "" })); + }; // Fetch and save the users. // This follows useSWR's recommendation for simple pagination: // https://swr.vercel.app/docs/pagination#when-to-use-useswr const display_name = filterValues.find((value) => value.id === "display_name")?.value ?? ""; - useSWR( - `/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}&display_name=${display_name}`, - get, - { - onSuccess: (data) => { - // When no more users can be found, trigger a toast to indicate why no - // changes have taken place. We have to maintain a non-empty set of - // users otherwise we can't paginate using a cursor (since we've lost the - // cursor). - if (data.length === 0) { - toast({ - title: "No more users", - status: "warning", - duration: 1000, - isClosable: true, - }); - return; - } - setUsers(data); - }, - } - ); + useSWR< + FetchUsersResponse + >(`/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}&searchDisplayName=${display_name}&sortKey=display_name`, get, { + onSuccess: (data) => { + // When no more users can be found, trigger a toast to indicate why no + // changes have taken place. We have to maintain a non-empty set of + // users otherwise we can't paginate using a cursor (since we've lost the + // cursor). + if (data.items.length === 0) { + toast({ + title: "No more users", + status: "warning", + duration: 1000, + isClosable: true, + }); + return; + } + setResponse(data); + }, + }); const toPreviousPage = () => { - if (users.length >= 0) { + if (response.items.length >= 0) { setPagination({ - cursor: users[0].user_id, + cursor: response.prev, direction: "back", }); } else { @@ -104,9 +109,9 @@ export const UserTable = memo(function UserTable() { }; const toNextPage = () => { - if (users.length >= 0) { + if (response.items.length >= 0) { setPagination({ - cursor: users[users.length - 1].user_id, + cursor: response.next, direction: "forward", }); } else { @@ -121,13 +126,13 @@ export const UserTable = memo(function UserTable() { return ( ); }); diff --git a/website/src/lib/oasst_api_client.ts b/website/src/lib/oasst_api_client.ts index 7db6e3c2..50adf267 100644 --- a/website/src/lib/oasst_api_client.ts +++ b/website/src/lib/oasst_api_client.ts @@ -1,7 +1,7 @@ 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"; +import type { BackendUser, BackendUserCore, User } from "src/types/Users"; export class OasstError { message: string; @@ -15,6 +15,22 @@ export class OasstError { } } +export type FetchUsersParams = { + limit: number; + cursor?: string; + direction: "forward" | "back"; + searchDisplayName?: string; + sortKey?: "username" | "display_name"; +}; + +export type FetchUsersResponse = { + items: T[]; + next?: string; + prev?: string; + sort_key: "username" | "display_name"; + order: "asc" | "desc"; +}; + export class OasstApiClient { oasstApiUrl: string; oasstApiKey: string; @@ -164,30 +180,39 @@ export class OasstApiClient { * forward. If false and `cursor` is not empty, pages backwards. * @returns {Promise} A Promise that returns an array of `BackendUser` objects. */ - async fetch_users(max_count: number, cursor: string, isForward: boolean): Promise { - const params = new URLSearchParams(); - params.append("max_count", max_count.toString()); + async fetch_users({ + direction, + limit, + cursor, + searchDisplayName, + sortKey = "display_name", + }: FetchUsersParams): Promise { + const params = new URLSearchParams({ + search_text: searchDisplayName, + sort_key: sortKey, + max_count: limit.toString(), + }); // The backend API uses different query parameters depending on the // pagination direction but they both take the same cursor value. // Depending on direction, pick the right query param. if (cursor !== "") { - params.append(isForward ? "gt" : "lt", cursor); + params.append(direction === "forward" ? "gt" : "lt", cursor); } - const BASE_URL = `/api/v1/frontend_users`; + const BASE_URL = `/api/v1/users/cursor`; const url = `${BASE_URL}/?${params.toString()}`; return this.get(url); } - async fetch_user_by_display_name(name: string): Promise { - const params = new URLSearchParams({ - search_text: name, - }); + // async fetch_user_by_display_name(name: string): Promise { + // const params = new URLSearchParams({ + // search_text: name, + // }); - const endpoint = `/api/v1/frontend_users/by_display_name`; + // const endpoint = `/api/v1/frontend_users/by_display_name`; - return this.get(`${endpoint}?${params.toString()}`); - } + // return this.get(`${endpoint}?${params.toString()}`); + // } /** * Returns the `Message`s associated with `user_id` in the backend. diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index 52921213..f43af305 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -1,12 +1,11 @@ import { withRole } from "src/lib/auth"; -import { oasstApiClient } from "src/lib/oasst_api_client"; +import { FetchUsersParams, oasstApiClient } from "src/lib/oasst_api_client"; import prisma from "src/lib/prismadb"; -import { BackendUser } from "src/types/Users"; /** * The number of users to fetch in a single request. Could later be a query parameter. */ -const PAGE_SIZE = 20; +const PAGE_SIZE = 2; /** * Returns a list of user results from the database when the requesting user is @@ -18,16 +17,16 @@ const PAGE_SIZE = 20; * direction. */ const handler = withRole("admin", async (req, res) => { - const { cursor, direction, display_name = "" } = req.query; + const { cursor, direction, searchDisplayName = "", sortKey = "username" } = req.query; // First, get all the users according to the backend. - let all_users: BackendUser[] = []; - - if (typeof display_name === "string" && display_name) { - all_users = await oasstApiClient.fetch_user_by_display_name(display_name); - } else { - all_users = await oasstApiClient.fetch_users(PAGE_SIZE, cursor as string, direction === "forward"); - } + const { items: all_users, ...rest } = await oasstApiClient.fetch_users({ + searchDisplayName: searchDisplayName as FetchUsersParams["searchDisplayName"], + direction: direction as FetchUsersParams["direction"], + limit: PAGE_SIZE, + cursor: cursor as FetchUsersParams["cursor"], + sortKey: sortKey === "username" || sortKey === "display_name" ? sortKey : undefined, + }); // Next, get all the users stored in the web's auth database to fetch their role. const local_user_ids = all_users.map(({ id }) => id); @@ -58,7 +57,10 @@ const handler = withRole("admin", async (req, res) => { }; }); - res.status(200).json(users); + res.status(200).json({ + items: users, + ...rest, + }); }); export default handler; From 77210ee6d41100a912498dbe898901f6d605ffa5 Mon Sep 17 00:00:00 2001 From: notmd Date: Sat, 21 Jan 2023 14:37:05 +0700 Subject: [PATCH 05/17] remove debug code --- website/src/pages/api/admin/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index f43af305..57944cff 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -5,7 +5,7 @@ import prisma from "src/lib/prismadb"; /** * The number of users to fetch in a single request. Could later be a query parameter. */ -const PAGE_SIZE = 2; +const PAGE_SIZE = 20; /** * Returns a list of user results from the database when the requesting user is From 27e1e549c42e830f6b560a52c3b595eddf7cc6d3 Mon Sep 17 00:00:00 2001 From: notmd Date: Sat, 21 Jan 2023 20:10:19 +0700 Subject: [PATCH 06/17] fix query in backward direction --- backend/oasst_backend/user_repository.py | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/backend/oasst_backend/user_repository.py b/backend/oasst_backend/user_repository.py index 1e9ac78f..e960d944 100644 --- a/backend/oasst_backend/user_repository.py +++ b/backend/oasst_backend/user_repository.py @@ -200,6 +200,7 @@ class UserRepository: search_text: Optional[str] = None, limit: Optional[int] = 100, ) -> list[User]: + if not self.api_client.trusted: if not api_client_id: # Let unprivileged api clients query their own users without api_client_id being set @@ -226,11 +227,15 @@ class UserRepository: if lte_display_name is not None: if lt_id: - qry = qry.filter( - or_( - User.display_name < lte_display_name, - and_(User.display_name == lte_display_name, User.id < lt_id), + qry = ( + qry.filter( + or_( + User.display_name < lte_display_name, + and_(User.display_name == lte_display_name, User.id < lt_id), + ) ) + .order_by(None) + .order_by(User.display_name.desc(), User.id.desc()) ) else: qry = qry.filter(User.display_name <= lte_display_name) @@ -252,4 +257,9 @@ class UserRepository: if limit is not None: qry = qry.limit(limit) - return qry.all() + users = qry.all() + + if lte_display_name and lt_id: + users.reverse() + + return users From c1dd188cbea8e88a945b8dc190c5e32dc1872c90 Mon Sep 17 00:00:00 2001 From: notmd Date: Sat, 21 Jan 2023 23:12:19 +0700 Subject: [PATCH 07/17] use pre and next cursor check from the server --- website/src/components/DataTable.tsx | 94 +++++++++++++++------------- website/src/components/UserTable.tsx | 94 ++++++++++------------------ website/src/pages/api/admin/users.ts | 2 +- 3 files changed, 84 insertions(+), 106 deletions(-) diff --git a/website/src/components/DataTable.tsx b/website/src/components/DataTable.tsx index eafca6d1..1784650a 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, @@ -47,6 +45,8 @@ export type DataTableProps = { onNextClick?: () => void; onPreviousClick?: () => void; onFilterChange?: (items: FilterItem[]) => void; + disableNext?: boolean; + disablePrevious?: boolean; }; export const DataTable = ({ @@ -57,6 +57,8 @@ export const DataTable = ({ onNextClick, onPreviousClick, onFilterChange, + disableNext, + disablePrevious, }: DataTableProps) => { const { getHeaderGroups, getRowModel } = useReactTable({ data, @@ -75,49 +77,51 @@ export const DataTable = ({ onFilterChange(newValues); }; return ( - - - - - - - - - - {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/UserTable.tsx b/website/src/components/UserTable.tsx index 68285bfa..1f4ccfca 100644 --- a/website/src/components/UserTable.tsx +++ b/website/src/components/UserTable.tsx @@ -1,4 +1,4 @@ -import { IconButton, useToast } from "@chakra-ui/react"; +import { Card, CardBody, IconButton } from "@chakra-ui/react"; import { createColumnHelper } from "@tanstack/react-table"; import Link from "next/link"; import { memo, useState } from "react"; @@ -57,11 +57,7 @@ const columns: DataTableColumnDef[] = [ ]; export const UserTable = memo(function UserTable() { - const toast = useToast(); const [pagination, setPagination] = useState({ cursor: "", direction: "forward" }); - const [response, setResponse] = useState, "sort_key" | "order">>({ - items: [], - }); const [filterValues, setFilterValues] = useState([]); const handleFilterValuesChange = (values: FilterItem[]) => { setFilterValues(values); @@ -71,68 +67,46 @@ export const UserTable = memo(function UserTable() { // This follows useSWR's recommendation for simple pagination: // https://swr.vercel.app/docs/pagination#when-to-use-useswr const display_name = filterValues.find((value) => value.id === "display_name")?.value ?? ""; - useSWR< - FetchUsersResponse - >(`/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}&searchDisplayName=${display_name}&sortKey=display_name`, get, { - onSuccess: (data) => { - // When no more users can be found, trigger a toast to indicate why no - // changes have taken place. We have to maintain a non-empty set of - // users otherwise we can't paginate using a cursor (since we've lost the - // cursor). - if (data.items.length === 0) { - toast({ - title: "No more users", - status: "warning", - duration: 1000, - isClosable: true, - }); - return; - } - setResponse(data); - }, - }); + const { data, error } = useSWR>( + `/api/admin/users?direction=${pagination.direction}&cursor=${pagination.cursor}&searchDisplayName=${display_name}&sortKey=display_name`, + get, + { + keepPreviousData: true, + } + ); const toPreviousPage = () => { - if (response.items.length >= 0) { - setPagination({ - cursor: response.prev, - direction: "back", - }); - } else { - toast({ - title: "Can not paginate when no users are found", - status: "warning", - duration: 1000, - isClosable: true, - }); - } + setPagination({ + cursor: data.prev, + direction: "back", + }); }; const toNextPage = () => { - if (response.items.length >= 0) { - setPagination({ - cursor: response.next, - direction: "forward", - }); - } else { - toast({ - title: "Can not paginate when no users are found", - status: "warning", - duration: 1000, - isClosable: true, - }); - } + setPagination({ + cursor: data.next, + direction: "forward", + }); }; return ( - + + + {data && ( + + )} + {error && "Unable to load users."} + + ); }); diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index 57944cff..f43af305 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -5,7 +5,7 @@ import prisma from "src/lib/prismadb"; /** * The number of users to fetch in a single request. Could later be a query parameter. */ -const PAGE_SIZE = 20; +const PAGE_SIZE = 2; /** * Returns a list of user results from the database when the requesting user is From aebfaacac8e5abb8a5b5097ea6f30d68afd9763c Mon Sep 17 00:00:00 2001 From: notmd Date: Sat, 21 Jan 2023 23:21:10 +0700 Subject: [PATCH 08/17] remove debug code --- website/src/pages/api/admin/users.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts index f43af305..57944cff 100644 --- a/website/src/pages/api/admin/users.ts +++ b/website/src/pages/api/admin/users.ts @@ -5,7 +5,7 @@ import prisma from "src/lib/prismadb"; /** * The number of users to fetch in a single request. Could later be a query parameter. */ -const PAGE_SIZE = 2; +const PAGE_SIZE = 20; /** * Returns a list of user results from the database when the requesting user is From 15acd1c64e917910e68b32c800f23ff516655d43 Mon Sep 17 00:00:00 2001 From: notmd Date: Sat, 21 Jan 2023 23:56:21 +0700 Subject: [PATCH 09/17] handle error --- website/src/components/DataTable.tsx | 94 +++++++++++++++------------- website/src/components/UserTable.tsx | 34 +++++----- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/website/src/components/DataTable.tsx b/website/src/components/DataTable.tsx index 1784650a..f9ef4e49 100644 --- a/website/src/components/DataTable.tsx +++ b/website/src/components/DataTable.tsx @@ -1,6 +1,8 @@ import { Box, Button, + Card, + CardBody, Flex, FormControl, FormLabel, @@ -77,51 +79,53 @@ export const DataTable = ({ onFilterChange(newValues); }; return ( - <> - - - - - - - - {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/UserTable.tsx b/website/src/components/UserTable.tsx index 1f4ccfca..df412bbc 100644 --- a/website/src/components/UserTable.tsx +++ b/website/src/components/UserTable.tsx @@ -1,4 +1,4 @@ -import { Card, CardBody, IconButton } from "@chakra-ui/react"; +import { IconButton } from "@chakra-ui/react"; import { createColumnHelper } from "@tanstack/react-table"; import Link from "next/link"; import { memo, useState } from "react"; @@ -90,23 +90,19 @@ export const UserTable = memo(function UserTable() { }; return ( - - - {data && ( - - )} - {error && "Unable to load users."} - - + <> + + {error && "Unable to load users."} + ); }); From 6945cc5fe7c7028901c0834ea8d24a9ef628c268 Mon Sep 17 00:00:00 2001 From: notmd Date: Sun, 22 Jan 2023 14:14:41 +0700 Subject: [PATCH 10/17] remove `reverse` method --- backend/oasst_backend/user_repository.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/backend/oasst_backend/user_repository.py b/backend/oasst_backend/user_repository.py index 670bbc3e..118f4e82 100644 --- a/backend/oasst_backend/user_repository.py +++ b/backend/oasst_backend/user_repository.py @@ -269,9 +269,4 @@ class UserRepository: if limit is not None: qry = qry.limit(limit) - users = qry.all() - - if lte_display_name and lt_id: - users.reverse() - - return users + return qry.all() From 101f2c536a3ebbfe744637fc9bace22768755eb3 Mon Sep 17 00:00:00 2001 From: notmd Date: Sun, 22 Jan 2023 14:20:39 +0700 Subject: [PATCH 11/17] revert change in user_repository --- backend/oasst_backend/user_repository.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/backend/oasst_backend/user_repository.py b/backend/oasst_backend/user_repository.py index 118f4e82..79df99ab 100644 --- a/backend/oasst_backend/user_repository.py +++ b/backend/oasst_backend/user_repository.py @@ -233,15 +233,11 @@ class UserRepository: if lte_display_name is not None: if lt_id: - qry = ( - qry.filter( - or_( - User.display_name < lte_display_name, - and_(User.display_name == lte_display_name, User.id < lt_id), - ) + qry = qry.filter( + or_( + User.display_name < lte_display_name, + and_(User.display_name == lte_display_name, User.id < lt_id), ) - .order_by(None) - .order_by(User.display_name.desc(), User.id.desc()) ) else: qry = qry.filter(User.display_name <= lte_display_name) From 952c61f4613a74e387c1819b56ea7a0fd12e9c44 Mon Sep 17 00:00:00 2001 From: notmd Date: Sun, 22 Jan 2023 15:50:23 +0700 Subject: [PATCH 12/17] Fix recent messages --- website/src/pages/api/messages/user.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/website/src/pages/api/messages/user.ts b/website/src/pages/api/messages/user.ts index e5f361b8..bd651acc 100644 --- a/website/src/pages/api/messages/user.ts +++ b/website/src/pages/api/messages/user.ts @@ -4,6 +4,7 @@ const handler = withoutRole("banned", async (req, res, token) => { //TODO: add params if needed const params = new URLSearchParams({ username: token.sub, + auth_method: "local", }); const messagesRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages?${params}`, { From 554e730d348f766e49af2e71760c303757919815 Mon Sep 17 00:00:00 2001 From: notmd Date: Sun, 22 Jan 2023 16:01:01 +0700 Subject: [PATCH 13/17] user `getBackendUserCore` --- website/src/pages/api/messages/user.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/website/src/pages/api/messages/user.ts b/website/src/pages/api/messages/user.ts index bd651acc..cbf658f1 100644 --- a/website/src/pages/api/messages/user.ts +++ b/website/src/pages/api/messages/user.ts @@ -1,10 +1,12 @@ import { withoutRole } from "src/lib/auth"; +import { getBackendUserCore } from "src/lib/users"; const handler = withoutRole("banned", async (req, res, token) => { //TODO: add params if needed + const user = await getBackendUserCore(token.sub); const params = new URLSearchParams({ username: token.sub, - auth_method: "local", + auth_method: user.auth_method, }); const messagesRes = await fetch(`${process.env.FASTAPI_URL}/api/v1/messages?${params}`, { From 0f0d0e00b5494775355035aa9c5cad7cd170d7d9 Mon Sep 17 00:00:00 2001 From: notmd Date: Sun, 22 Jan 2023 17:05:22 +0700 Subject: [PATCH 14/17] use `user.id` --- website/src/pages/api/messages/user.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/pages/api/messages/user.ts b/website/src/pages/api/messages/user.ts index cbf658f1..6f39aad1 100644 --- a/website/src/pages/api/messages/user.ts +++ b/website/src/pages/api/messages/user.ts @@ -5,7 +5,7 @@ const handler = withoutRole("banned", async (req, res, token) => { //TODO: add params if needed const user = await getBackendUserCore(token.sub); const params = new URLSearchParams({ - username: token.sub, + username: user.id, auth_method: user.auth_method, }); From 6167f63467e0bc98a644b52f9dd57090e1b55287 Mon Sep 17 00:00:00 2001 From: AbdBarho Date: Sun, 22 Jan 2023 11:53:34 +0100 Subject: [PATCH 15/17] Present available task types on dashboard --- .../src/components/Dashboard/TaskOption.tsx | 77 +++++++++++-------- website/src/components/Tasks/Task/Task.tsx | 4 +- website/src/components/Tasks/TaskTypes.tsx | 8 +- website/src/pages/dashboard.tsx | 10 ++- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/website/src/components/Dashboard/TaskOption.tsx b/website/src/components/Dashboard/TaskOption.tsx index e2bafac3..5a759d40 100644 --- a/website/src/components/Dashboard/TaskOption.tsx +++ b/website/src/components/Dashboard/TaskOption.tsx @@ -1,48 +1,61 @@ import { Box, Flex, GridItem, Heading, SimpleGrid, Text, useColorModeValue } from "@chakra-ui/react"; import Link from "next/link"; +import { useMemo } from "react"; +import { TaskType } from "src/types/Task"; -import { TaskCategory, TaskCategoryLabels, TaskTypes } from "../Tasks/TaskTypes"; +import { TaskCategory, TaskCategoryLabels, TaskInfo, TaskInfos } from "../Tasks/TaskTypes"; -export const TaskOption = ({ displayTaskCategories }: { displayTaskCategories: TaskCategory[] }) => { +export interface TasksOptionProps { + content: Partial>; +} + +export const TaskOption = ({ content }: TasksOptionProps) => { const backgroundColor = useColorModeValue("white", "gray.700"); + const taskInfoMap = useMemo( + () => + Object.values(content) + .flat() + .reduce((obj, taskType) => { + obj[taskType] = TaskInfos.filter((t) => t.type === taskType).pop(); + return obj; + }, {} as Record), + [content] + ); + return ( - {displayTaskCategories.map((category) => ( + {Object.entries(content).map(([category, taskTypes]) => (
- {TaskCategoryLabels[category]} + + {TaskCategoryLabels[category]} + - {TaskTypes.filter((task) => task.category === category).map((item) => ( - - - - - - {item.label} - - - {item.desc} - - - - taskInfoMap[taskType]) + .map((item) => ( + + - + + {item.label} + {item.desc} + + Go -> - - - - ))} + + + ))}
))} diff --git a/website/src/components/Tasks/Task/Task.tsx b/website/src/components/Tasks/Task/Task.tsx index 3d393575..45fb83d0 100644 --- a/website/src/components/Tasks/Task/Task.tsx +++ b/website/src/components/Tasks/Task/Task.tsx @@ -3,7 +3,7 @@ import { TaskControls } from "src/components/Survey/TaskControls"; import { CreateTask } from "src/components/Tasks/CreateTask"; import { EvaluateTask } from "src/components/Tasks/EvaluateTask"; import { LabelTask } from "src/components/Tasks/LabelTask"; -import { TaskCategory, TaskInfo, TaskTypes } from "src/components/Tasks/TaskTypes"; +import { TaskCategory, TaskInfo, TaskInfos } from "src/components/Tasks/TaskTypes"; import { UnchangedWarning } from "src/components/Tasks/UnchangedWarning"; import { post } from "src/lib/api"; import { TaskContent } from "src/types/Task"; @@ -29,7 +29,7 @@ export const Task = ({ frontendId, task, trigger, mutate }) => { const rootEl = useRef(null); - const taskType = TaskTypes.find((taskType) => taskType.type === task.type && taskType.mode === task.mode); + const taskType = TaskInfos.find((taskType) => taskType.type === task.type && taskType.mode === task.mode); const { trigger: sendRejection } = useSWRMutation("/api/reject_task", post, { onSuccess: async () => { diff --git a/website/src/components/Tasks/TaskTypes.tsx b/website/src/components/Tasks/TaskTypes.tsx index 4c6da92c..d10159d9 100644 --- a/website/src/components/Tasks/TaskTypes.tsx +++ b/website/src/components/Tasks/TaskTypes.tsx @@ -21,16 +21,16 @@ export interface TaskInfo { } export const TaskCategoryLabels: { [key in TaskCategory]: string } = { - [TaskCategory.Random]: "I'm feeling lucky", + [TaskCategory.Random]: "Grab a task!", [TaskCategory.Create]: "Create", [TaskCategory.Evaluate]: "Evaluate", [TaskCategory.Label]: "Label", }; -export const TaskTypes: TaskInfo[] = [ +export const TaskInfos: TaskInfo[] = [ // general/random { - label: "Start a Task", + label: "I'm feeling lucky", desc: "Help us improve Open Assistant by starting a random task.", category: TaskCategory.Random, pathname: "/tasks/random", @@ -104,7 +104,7 @@ export const TaskTypes: TaskInfo[] = [ category: TaskCategory.Evaluate, pathname: "/evaluate/rank_initial_prompts", help_link: "https://projects.laion.ai/Open-Assistant/docs/guides/prompting", - overview: "Given the following inital prompts, sort them from best to worst, best being first, worst being last.", + overview: "Given the following initial prompts, sort them from best to worst, best being first, worst being last.", type: "rank_initial_prompts", update_type: "message_ranking", unchanged_title: "Order Unchanged", diff --git a/website/src/pages/dashboard.tsx b/website/src/pages/dashboard.tsx index e0b8bba4..4def6196 100644 --- a/website/src/pages/dashboard.tsx +++ b/website/src/pages/dashboard.tsx @@ -5,15 +5,17 @@ import { LeaderboardTable, TaskOption, WelcomeCard } from "src/components/Dashbo 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"; +import { 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]); + const availableTaskTypes = useMemo(() => { + const taskTypes = filterAvailableTasks(data ?? {}); + return { [TaskCategory.Random]: taskTypes }; + }, [data]); return ( <> @@ -23,7 +25,7 @@ const Dashboard = () => { - + From fd703663bd1bea984a09869da6124d4dd49641a2 Mon Sep 17 00:00:00 2001 From: AbdBarho Date: Sun, 22 Jan 2023 12:00:55 +0100 Subject: [PATCH 16/17] Fix all tasks page --- website/src/components/Dashboard/TaskOption.tsx | 11 +++++++++++ website/src/pages/tasks/all.tsx | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/website/src/components/Dashboard/TaskOption.tsx b/website/src/components/Dashboard/TaskOption.tsx index 5a759d40..e73a06c8 100644 --- a/website/src/components/Dashboard/TaskOption.tsx +++ b/website/src/components/Dashboard/TaskOption.tsx @@ -62,3 +62,14 @@ export const TaskOption = ({ content }: TasksOptionProps) => {
); }; + +export const allTaskOptions: TasksOptionProps["content"] = { + [TaskCategory.Random]: [TaskType.random], + [TaskCategory.Create]: [TaskType.initial_prompt, TaskType.prompter_reply, TaskType.assistant_reply], + [TaskCategory.Evaluate]: [ + TaskType.rank_initial_prompts, + TaskType.rank_prompter_replies, + TaskType.rank_assistant_replies, + ], + [TaskCategory.Label]: [TaskType.label_initial_prompt, TaskType.label_prompter_reply, TaskType.label_assistant_reply], +}; diff --git a/website/src/pages/tasks/all.tsx b/website/src/pages/tasks/all.tsx index 3ccfd4e8..01954c2f 100644 --- a/website/src/pages/tasks/all.tsx +++ b/website/src/pages/tasks/all.tsx @@ -1,7 +1,7 @@ import Head from "next/head"; import { TaskOption } from "src/components/Dashboard"; +import { allTaskOptions } from "src/components/Dashboard/TaskOption"; import { getDashboardLayout } from "src/components/Layout"; -import { TaskCategory } from "src/components/Tasks/TaskTypes"; export { getDefaultStaticProps as getStaticProps } from "src/lib/default_static_props"; const AllTasks = () => { @@ -11,7 +11,7 @@ const AllTasks = () => { All Tasks - Open Assistant - + ); }; From fb4e94487cd407c941096400e10c4704c88a6913 Mon Sep 17 00:00:00 2001 From: AbdBarho Date: Sun, 22 Jan 2023 12:15:21 +0100 Subject: [PATCH 17/17] Redirect users to dashboard if there are no tasks --- website/src/components/EmptyState.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/website/src/components/EmptyState.tsx b/website/src/components/EmptyState.tsx index 14715518..51e51a00 100644 --- a/website/src/components/EmptyState.tsx +++ b/website/src/components/EmptyState.tsx @@ -1,5 +1,5 @@ -import { Box, Link, Text, useColorModeValue } from "@chakra-ui/react"; -import { useRouter } from "next/router"; +import { Box, Text, useColorModeValue } from "@chakra-ui/react"; +import NextLink from "next/link"; import { FiAlertTriangle } from "react-icons/fi"; import { IconType } from "react-icons/lib"; @@ -10,16 +10,15 @@ type EmptyStateProps = { export const EmptyState = (props: EmptyStateProps) => { const backgroundColor = useColorModeValue("white", "gray.800"); - const router = useRouter(); return ( {props.text} - router.back()} color="blue.500" textUnderlineOffset="3px"> - Click here to go back - + + Go back to the dashboard + );