diff --git a/src/app/(auth)/communities/page.tsx b/src/app/(auth)/communities/page.tsx index f15d5eb..9badcd6 100644 --- a/src/app/(auth)/communities/page.tsx +++ b/src/app/(auth)/communities/page.tsx @@ -1,20 +1,5 @@ import Communities from "@/components/organisms/Communities"; -// import { ApiResponse, Community } from "@/lib/api"; -// import { NODES_API_URL } from "@/lib/config"; -// import { cookies } from "next/headers"; export default async function CommunityPage() { - // const cookie = cookies().getAll(); - // console.log('cookies', cookie) - // const response = await fetch(`${NODES_API_URL}/v1/admin/communities`, { - // headers: { 'cookie': cookies().toString()}, - // credentials: "include", - // }); - // console.log("get list", response.ok, response.status); - // // if (response.ok) { - // const json = (await response.json()) as ApiResponse; - // const data = json.data; - - // console.log('COMMUNITY', data ) return ; } diff --git a/src/app/(auth)/users/page.tsx b/src/app/(auth)/users/page.tsx index 51b96e7..161ac5a 100644 --- a/src/app/(auth)/users/page.tsx +++ b/src/app/(auth)/users/page.tsx @@ -1,30 +1,35 @@ +"use client"; import { DataTable } from "@/components/organisms/users-datatable/data-table"; -import { tasks } from "@/components/organisms/users-datatable/data/tasks"; +import { users } from "@/components/organisms/users-datatable/data/users"; import { columns } from "@/components/organisms/users-datatable/columns"; +import { searchUsers } from "@/lib/api"; +import { useQuery } from "@tanstack/react-query"; +import { getQueryClient } from "@/lib/get-query-client"; +import { LoaderCircleIcon } from "lucide-react"; -interface User { - id: string; - name: string; - email: string; -} -const mockUsers: User[] = [ - { id: "1", name: "John Doe", email: "john@example.com" }, - { id: "2", name: "Jane Smith", email: "jane@example.com" }, -]; +const queryClient = getQueryClient(); export default function UsersTable() { + const { data, isLoading, error } = useQuery(searchUsers, queryClient); + return ( <>

Welcome back!

- Here's a list of your tasks for this month! + Here's a list of your users on nodes platform

- + {isLoading ? ( +
+ +
+ ) : ( + + )}
); diff --git a/src/app/actions.tsx b/src/app/actions.tsx index f6122d8..9901e82 100644 --- a/src/app/actions.tsx +++ b/src/app/actions.tsx @@ -15,12 +15,6 @@ export async function login(prevState: any, formData: FormData) { const email = formData.get("email") ?? prevState?.email; const code = formData.get("code"); - if (!email?.endsWith("@desci.com")) - return { - ok: false, - error: "Unauthorised email domain (only desci.com emails are allowed)", - }; - const res = await fetch(`${API_URL}/v1/auth/magic`, { method: "POST", body: JSON.stringify({ email, code }), diff --git a/src/components/molecules/CommunityMembers.tsx b/src/components/molecules/CommunityMembers.tsx index 853f71a..d58a9a0 100644 --- a/src/components/molecules/CommunityMembers.tsx +++ b/src/components/molecules/CommunityMembers.tsx @@ -28,7 +28,7 @@ import { CommandList, } from "@/components/ui/command"; import { Check, ChevronsUpDown, X, Settings } from "lucide-react"; -import { addMember, Community, removeMember, searchUsers } from "@/lib/api"; +import { addMember, Community, removeMember, searchUsers, searchUsersProfiles } from "@/lib/api"; import { useMutation, useQuery } from "@tanstack/react-query"; import { getQueryClient } from "@/lib/get-query-client"; import { tags } from "@/lib/tags"; @@ -94,7 +94,7 @@ export default function CommunityMembers({ const { mutate } = useMutation( { mutationKey: [tags.users], - mutationFn: searchUsers, + mutationFn: searchUsersProfiles, }, queryClient ); diff --git a/src/components/organisms/community-datatable/columns.tsx b/src/components/organisms/community-datatable/columns.tsx index ae37fe7..3ce469d 100644 --- a/src/components/organisms/community-datatable/columns.tsx +++ b/src/components/organisms/community-datatable/columns.tsx @@ -152,23 +152,23 @@ export const columns: ColumnDef[] = [ }, enableSorting: false, }, - { - accessorKey: "description", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - const label = row.original.description; + // { + // accessorKey: "description", + // header: ({ column }) => ( + // + // ), + // cell: ({ row }) => { + // const label = row.original.description; - return ( -
- - {label} - -
- ); - }, - }, + // return ( + //
+ // + // {label} + // + //
+ // ); + // }, + // }, { accessorKey: "keywords", header: ({ column }) => ( @@ -200,22 +200,22 @@ export const columns: ColumnDef[] = [ enableSorting: false, // enableResizing: true, }, - { - accessorKey: "image_url", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - return ( -
- - {row.getValue("image_url")} - -
- ); - }, - enableSorting: false, - }, + // { + // accessorKey: "image_url", + // header: ({ column }) => ( + // + // ), + // cell: ({ row }) => { + // return ( + //
+ // + // {row.getValue("image_url")} + // + //
+ // ); + // }, + // enableSorting: false, + // }, { accessorKey: "createdAt", header: ({ column }) => ( @@ -231,21 +231,21 @@ export const columns: ColumnDef[] = [ ); }, }, - { - accessorKey: "updatedAt", - header: ({ column }) => ( - - ), - cell: ({ row }) => { - return ( -
- - {row.getValue("updatedAt")} - -
- ); - }, - }, + // { + // accessorKey: "updatedAt", + // header: ({ column }) => ( + // + // ), + // cell: ({ row }) => { + // return ( + //
+ // + // {row.getValue("updatedAt")} + // + //
+ // ); + // }, + // }, { id: "actions", cell: ({ row }) => , diff --git a/src/components/organisms/users-datatable/columns.tsx b/src/components/organisms/users-datatable/columns.tsx index a5fc6bf..8050dd4 100644 --- a/src/components/organisms/users-datatable/columns.tsx +++ b/src/components/organisms/users-datatable/columns.tsx @@ -1,123 +1,171 @@ "use client"; -import { ColumnDef } from '@tanstack/react-table' +import { ColumnDef } from "@tanstack/react-table"; -import { Badge } from '@/components/ui/badge' -import { Checkbox } from '@/components/ui/checkbox' -import { DataTableColumnHeader } from './data-table-column-header' -import { DataTableRowActions } from './data-table-row-actions' +import { Checkbox } from "@/components/ui/checkbox"; +import { DataTableColumnHeader } from "./data-table-column-header"; +import { DataTableRowActions } from "./data-table-row-actions"; -import { labels, priorities, statuses } from './data/data' -import { Task } from './data/schema' +import { roles } from "./data/data"; +import { User } from "./data/schema"; +import { CircleIcon, QuestionMarkCircledIcon } from "@radix-ui/react-icons"; +import { DicesIcon } from "lucide-react"; -export const columns: ColumnDef[] = [ +export const columns: ColumnDef[] = [ { - id: 'select', + id: "select", header: ({ table }) => ( table.toggleAllPageRowsSelected(!!value)} - aria-label='Select all' - className='translate-y-[2px]' + aria-label="Select all" + className="translate-y-[2px]" /> ), cell: ({ row }) => ( row.toggleSelected(!!value)} - aria-label='Select row' - className='translate-y-[2px]' + aria-label="Select row" + className="translate-y-[2px]" /> ), enableSorting: false, enableHiding: false, }, { - accessorKey: 'id', + accessorKey: "id", header: ({ column }) => ( - + ), - cell: ({ row }) =>
{row.getValue('id')}
, + cell: ({ row }) =>
{row.getValue("id")}
, enableSorting: false, enableHiding: false, }, { - accessorKey: 'title', + accessorKey: "name", header: ({ column }) => ( - + ), cell: ({ row }) => { - const label = labels.find((label) => label.value === row.original.label) - return ( -
- {label && {label.label}} - - {row.getValue('title')} +
+ + {row.getValue("name")}
- ) + ); + }, + filterFn: (row, id, value) => { + console.log("name filter", { id, value, row: row.getValue(id) }); + return ( + (row.getValue(id) as string) + ?.toLowerCase() + ?.includes((value as string).toLowerCase()) || + (row.getValue("email") as string) + ?.toLowerCase() + ?.includes((value as string).toLowerCase()) + ); }, }, { - accessorKey: 'status', + accessorKey: "email", header: ({ column }) => ( - + ), cell: ({ row }) => { - const status = statuses.find( - (status) => status.value === row.getValue('status') - ) - - if (!status) { - return null - } - + const isDesciEmail = (row.getValue("email") as string)?.endsWith("@desci.com"); + console.log('IsDesci', { isDesciEmail, email: (row.getValue("email") as string)}) return ( -
- {status.icon && ( - - )} - {status.label} +
+ + {row.getValue("email")} + {isDesciEmail ? ( + + ) : null}
- ) - }, - filterFn: (row, id, value) => { - return value.includes(row.getValue(id)) + ); }, }, { - accessorKey: 'priority', + accessorKey: "isAdmin", header: ({ column }) => ( - + ), cell: ({ row }) => { - const priority = priorities.find( - (priority) => priority.value === row.getValue('priority') - ) + const isAdmin = row.getValue("isAdmin") === true; + const role = roles.find((status) => + isAdmin ? status.value === "admin" : status.value === "user" + ); - if (!priority) { - return null + if (!role) { + return null; } return ( -
- {priority.icon && ( - +
+ {role.icon && ( + )} - {priority.label} + {isAdmin ? "Admin" : "User"}
- ) + ); }, filterFn: (row, id, value) => { - return value.includes(row.getValue(id)) + const isAdmin = row.getValue("isAdmin") === true; + // const include = [isAdmin ? 'admin' : 'user'] + // console.log('filter', { isAdmin, filter: value.includes(row.getValue(id)), value, id } ) + return value.includes(isAdmin ? "admin" : "user"); + }, + }, + // { + // accessorKey: 'priority', + // header: ({ column }) => ( + // + // ), + // cell: ({ row }) => { + // const priority = priorities.find( + // (priority) => priority.value === row.getValue('priority') + // ) + + // if (!priority) { + // return null + // } + + // return ( + //
+ // {priority.icon && ( + // + // )} + // {priority.label} + //
+ // ) + // }, + // filterFn: (row, id, value) => { + // return value.includes(row.getValue(id)) + // }, + // }, + { + accessorKey: "createdAt", + header: ({ column }) => ( + + ), + cell: ({ row }) => { + return ( +
+ + {row.getValue("createdAt")} + +
+ ); }, }, { - id: 'actions', + id: "actions", cell: ({ row }) => , }, -] +]; diff --git a/src/components/organisms/users-datatable/data-table-row-actions.tsx b/src/components/organisms/users-datatable/data-table-row-actions.tsx index f10f08f..a0f0561 100644 --- a/src/components/organisms/users-datatable/data-table-row-actions.tsx +++ b/src/components/organisms/users-datatable/data-table-row-actions.tsx @@ -1,7 +1,7 @@ -import { DotsHorizontalIcon } from '@radix-ui/react-icons' -import { Row } from '@tanstack/react-table' +import { DotsHorizontalIcon } from "@radix-ui/react-icons"; +import { Row } from "@tanstack/react-table"; -import { LoaderButton } from '@/components/custom/LoaderButton' +import { LoaderButton } from "@/components/custom/LoaderButton"; import { DropdownMenu, DropdownMenuContent, @@ -14,54 +14,67 @@ import { DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, -} from '@/components/ui/dropdown-menu' +} from "@/components/ui/dropdown-menu"; -import { labels } from './data/data' -import { taskSchema } from './data/schema' +import { labels } from "./data/data"; +import { userSchema } from "./data/schema"; +import { useMutation } from "@tanstack/react-query"; +import { toggleUserRole } from "@/lib/api"; +import { getQueryClient } from "@/lib/get-query-client"; +import { tags } from "@/lib/tags"; interface DataTableRowActionsProps { - row: Row + row: Row; } export function DataTableRowActions({ row, }: DataTableRowActionsProps) { - const task = taskSchema.parse(row.original) + const queryClient = getQueryClient(); + const user = userSchema.parse(row.original); + const isAdmin = user.isAdmin; + + const toggleRoleMutation = useMutation({ + mutationFn: toggleUserRole, + }); + + const toggleRole = (id: number) => { + toggleRoleMutation.mutate( + { + userId: id, + }, + { + onSuccess() { + queryClient.invalidateQueries({ queryKey: [tags.users] }); + }, + } + ); + }; return ( - - Open menu + + Open menu - - Edit - Make a copy - Favorite - - - Labels - - - {labels.map((label) => ( - - {label.label} - - ))} - - - + + Edit + toggleRole(user.id)} className="cursor-pointer"> + + {isAdmin ? "Revoke admin privileges" : "Grant admin privileges"} + + - + Delete ⌘⌫ - ) + ); } diff --git a/src/components/organisms/users-datatable/data-table-toolbar.tsx b/src/components/organisms/users-datatable/data-table-toolbar.tsx index cb8b22f..899877d 100644 --- a/src/components/organisms/users-datatable/data-table-toolbar.tsx +++ b/src/components/organisms/users-datatable/data-table-toolbar.tsx @@ -1,62 +1,61 @@ -import { Cross2Icon } from '@radix-ui/react-icons' -import { Table } from '@tanstack/react-table' +import { Cross2Icon } from "@radix-ui/react-icons"; +import { Table } from "@tanstack/react-table"; +import { Input } from "@/components/ui/input"; +import { DataTableViewOptions } from "./data-table-view-options"; -import { Input } from '@/components/ui/input' -import { DataTableViewOptions } from './data-table-view-options' - -import { priorities, statuses } from './data/data' -import { DataTableFacetedFilter } from './data-table-faceted-filter' -import { Button } from '@/components/ui/button' +import { priorities, roles, statuses } from "./data/data"; +import { DataTableFacetedFilter } from "./data-table-faceted-filter"; +import { Button } from "@/components/ui/button"; interface DataTableToolbarProps { - table: Table + table: Table; } export function DataTableToolbar({ table, }: DataTableToolbarProps) { - const isFiltered = table.getState().columnFilters.length > 0 + const isFiltered = table.getState().columnFilters.length > 0; return ( -
-
+
+
- table.getColumn('title')?.setFilterValue(event.target.value) - } - className='h-8 w-[150px] lg:w-[250px]' + placeholder="Filter users..." + value={(table.getColumn("name")?.getFilterValue() as string) ?? ""} + onChange={(event) => { + table.getColumn("name")?.setFilterValue(event.target.value); + }} + className="h-8 w-[250px] lg:h-10 lg:w-[350px]" /> -
- {table.getColumn('status') && ( +
+ {table.getColumn("isAdmin") && ( )} - {table.getColumn('priority') && ( + {/* {table.getColumn('priority') && ( - )} + )} */}
{isFiltered && ( )}
- ) + ); } diff --git a/src/components/organisms/users-datatable/data/data.tsx b/src/components/organisms/users-datatable/data/data.tsx index 8ba5130..018ea52 100644 --- a/src/components/organisms/users-datatable/data/data.tsx +++ b/src/components/organisms/users-datatable/data/data.tsx @@ -8,6 +8,7 @@ import { QuestionMarkCircledIcon, StopwatchIcon, } from '@radix-ui/react-icons' +import { UserIcon, UserRoundPlusIcon } from 'lucide-react' export const labels = [ { @@ -52,6 +53,19 @@ export const statuses = [ }, ] +export const roles = [ + { + value: 'admin', + label: 'Admin', + icon: UserRoundPlusIcon + }, + { + value: 'user', + label: 'User', + icon: UserIcon + } +] + export const priorities = [ { label: 'Low', diff --git a/src/components/organisms/users-datatable/data/schema.ts b/src/components/organisms/users-datatable/data/schema.ts index deacb53..a28bdc0 100644 --- a/src/components/organisms/users-datatable/data/schema.ts +++ b/src/components/organisms/users-datatable/data/schema.ts @@ -1,13 +1,14 @@ -import { z } from 'zod' +import { z } from "zod"; // We're keeping a simple non-relational schema here. // IRL, you will have a schema for your data models. -export const taskSchema = z.object({ - id: z.string(), - title: z.string(), - status: z.string(), - label: z.string(), - priority: z.string(), -}) +export const userSchema = z.object({ + id: z.coerce.number(), + email: z.string().nullable().default('-'), + name: z.string().nullable().default('-'), + isAdmin: z.boolean(), + createdAt: z.string(), + orcid: z.string().optional().nullable().default('-'), +}); -export type Task = z.infer +export type User = z.infer; diff --git a/src/components/organisms/users-datatable/data/tasks.ts b/src/components/organisms/users-datatable/data/tasks.ts deleted file mode 100644 index fc6f3ba..0000000 --- a/src/components/organisms/users-datatable/data/tasks.ts +++ /dev/null @@ -1,782 +0,0 @@ -export const tasks = [ - { - id: 'TASK-8782', - title: - "You can't compress the program without quantifying the open-source SSD pixel!", - status: 'in progress', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-7878', - title: - 'Try to calculate the EXE feed, maybe it will index the multi-byte pixel!', - status: 'backlog', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-7839', - title: 'We need to bypass the neural TCP card!', - status: 'todo', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-5562', - title: - 'The SAS interface is down, bypass the open-source pixel so we can back up the PNG bandwidth!', - status: 'backlog', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-8686', - title: - "I'll parse the wireless SSL protocol, that should driver the API panel!", - status: 'canceled', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-1280', - title: - 'Use the digital TLS panel, then you can transmit the haptic system!', - status: 'done', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-7262', - title: - 'The UTF8 application is down, parse the neural bandwidth so we can back up the PNG firewall!', - status: 'done', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-1138', - title: - "Generating the driver won't do anything, we need to quantify the 1080p SMTP bandwidth!", - status: 'in progress', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-7184', - title: 'We need to program the back-end THX pixel!', - status: 'todo', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-5160', - title: - "Calculating the bus won't do anything, we need to navigate the back-end JSON protocol!", - status: 'in progress', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-5618', - title: - "Generating the driver won't do anything, we need to index the online SSL application!", - status: 'done', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-6699', - title: - "I'll transmit the wireless JBOD capacitor, that should hard drive the SSD feed!", - status: 'backlog', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-2858', - title: 'We need to override the online UDP bus!', - status: 'backlog', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-9864', - title: - "I'll reboot the 1080p FTP panel, that should matrix the HEX hard drive!", - status: 'done', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-8404', - title: 'We need to generate the virtual HEX alarm!', - status: 'in progress', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-5365', - title: - "Backing up the pixel won't do anything, we need to transmit the primary IB array!", - status: 'in progress', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-1780', - title: - 'The CSS feed is down, index the bluetooth transmitter so we can compress the CLI protocol!', - status: 'todo', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-6938', - title: - 'Use the redundant SCSI application, then you can hack the optical alarm!', - status: 'todo', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-9885', - title: 'We need to compress the auxiliary VGA driver!', - status: 'backlog', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-3216', - title: - "Transmitting the transmitter won't do anything, we need to compress the virtual HDD sensor!", - status: 'backlog', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-9285', - title: - 'The IP monitor is down, copy the haptic alarm so we can generate the HTTP transmitter!', - status: 'todo', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-1024', - title: - "Overriding the microchip won't do anything, we need to transmit the digital OCR transmitter!", - status: 'in progress', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-7068', - title: - "You can't generate the capacitor without indexing the wireless HEX pixel!", - status: 'canceled', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-6502', - title: - "Navigating the microchip won't do anything, we need to bypass the back-end SQL bus!", - status: 'todo', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-5326', - title: 'We need to hack the redundant UTF8 transmitter!', - status: 'todo', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-6274', - title: - 'Use the virtual PCI circuit, then you can parse the bluetooth alarm!', - status: 'canceled', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-1571', - title: - "I'll input the neural DRAM circuit, that should protocol the SMTP interface!", - status: 'in progress', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-9518', - title: - "Compressing the interface won't do anything, we need to compress the online SDD matrix!", - status: 'canceled', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-5581', - title: - "I'll synthesize the digital COM pixel, that should transmitter the UTF8 protocol!", - status: 'backlog', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-2197', - title: - "Parsing the feed won't do anything, we need to copy the bluetooth DRAM bus!", - status: 'todo', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-8484', - title: 'We need to parse the solid state UDP firewall!', - status: 'in progress', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-9892', - title: - 'If we back up the application, we can get to the UDP application through the multi-byte THX capacitor!', - status: 'done', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-9616', - title: 'We need to synthesize the cross-platform ASCII pixel!', - status: 'in progress', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-9744', - title: - 'Use the back-end IP card, then you can input the solid state hard drive!', - status: 'done', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-1376', - title: - "Generating the alarm won't do anything, we need to generate the mobile IP capacitor!", - status: 'backlog', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-7382', - title: - 'If we back up the firewall, we can get to the RAM alarm through the primary UTF8 pixel!', - status: 'todo', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-2290', - title: - "I'll compress the virtual JSON panel, that should application the UTF8 bus!", - status: 'canceled', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-1533', - title: - "You can't input the firewall without overriding the wireless TCP firewall!", - status: 'done', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-4920', - title: - "Bypassing the hard drive won't do anything, we need to input the bluetooth JSON program!", - status: 'in progress', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-5168', - title: - 'If we synthesize the bus, we can get to the IP panel through the virtual TLS array!', - status: 'in progress', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-7103', - title: 'We need to parse the multi-byte EXE bandwidth!', - status: 'canceled', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-4314', - title: - 'If we compress the program, we can get to the XML alarm through the multi-byte COM matrix!', - status: 'in progress', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-3415', - title: - 'Use the cross-platform XML application, then you can quantify the solid state feed!', - status: 'todo', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-8339', - title: - 'Try to calculate the DNS interface, maybe it will input the bluetooth capacitor!', - status: 'in progress', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-6995', - title: - 'Try to hack the XSS bandwidth, maybe it will override the bluetooth matrix!', - status: 'todo', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-8053', - title: - 'If we connect the program, we can get to the UTF8 matrix through the digital UDP protocol!', - status: 'todo', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-4336', - title: - 'If we synthesize the microchip, we can get to the SAS sensor through the optical UDP program!', - status: 'todo', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-8790', - title: - "I'll back up the optical COM alarm, that should alarm the RSS capacitor!", - status: 'done', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-8980', - title: - 'Try to navigate the SQL transmitter, maybe it will back up the virtual firewall!', - status: 'canceled', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-7342', - title: 'Use the neural CLI card, then you can parse the online port!', - status: 'backlog', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-5608', - title: - "I'll hack the haptic SSL program, that should bus the UDP transmitter!", - status: 'canceled', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-1606', - title: - "I'll generate the bluetooth PNG firewall, that should pixel the SSL driver!", - status: 'done', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-7872', - title: - "Transmitting the circuit won't do anything, we need to reboot the 1080p RSS monitor!", - status: 'canceled', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-4167', - title: - 'Use the cross-platform SMS circuit, then you can synthesize the optical feed!', - status: 'canceled', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-9581', - title: - "You can't index the port without hacking the cross-platform XSS monitor!", - status: 'backlog', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-8806', - title: 'We need to bypass the back-end SSL panel!', - status: 'done', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-6542', - title: - 'Try to quantify the RSS firewall, maybe it will quantify the open-source system!', - status: 'done', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-6806', - title: - 'The VGA protocol is down, reboot the back-end matrix so we can parse the CSS panel!', - status: 'canceled', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-9549', - title: "You can't bypass the bus without connecting the neural JBOD bus!", - status: 'todo', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-1075', - title: - "Backing up the driver won't do anything, we need to parse the redundant RAM pixel!", - status: 'done', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-1427', - title: - 'Use the auxiliary PCI circuit, then you can calculate the cross-platform interface!', - status: 'done', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-1907', - title: - "Hacking the circuit won't do anything, we need to back up the online DRAM system!", - status: 'todo', - label: 'documentation', - priority: 'high', - }, - { - id: 'TASK-4309', - title: - 'If we generate the system, we can get to the TCP sensor through the optical GB pixel!', - status: 'backlog', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-3973', - title: - "I'll parse the back-end ADP array, that should bandwidth the RSS bandwidth!", - status: 'todo', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-7962', - title: - 'Use the wireless RAM program, then you can hack the cross-platform feed!', - status: 'canceled', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-3360', - title: - "You can't quantify the program without synthesizing the neural OCR interface!", - status: 'done', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-9887', - title: - 'Use the auxiliary ASCII sensor, then you can connect the solid state port!', - status: 'backlog', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-3649', - title: - "I'll input the virtual USB system, that should circuit the DNS monitor!", - status: 'in progress', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-3586', - title: - 'If we quantify the circuit, we can get to the CLI feed through the mobile SMS hard drive!', - status: 'in progress', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-5150', - title: - "I'll hack the wireless XSS port, that should transmitter the IP interface!", - status: 'canceled', - label: 'feature', - priority: 'medium', - }, - { - id: 'TASK-3652', - title: - 'The SQL interface is down, override the optical bus so we can program the ASCII interface!', - status: 'backlog', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-6884', - title: - 'Use the digital PCI circuit, then you can synthesize the multi-byte microchip!', - status: 'canceled', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-1591', - title: 'We need to connect the mobile XSS driver!', - status: 'in progress', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-3802', - title: - 'Try to override the ASCII protocol, maybe it will parse the virtual matrix!', - status: 'in progress', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-7253', - title: - "Programming the capacitor won't do anything, we need to bypass the neural IB hard drive!", - status: 'backlog', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-9739', - title: 'We need to hack the multi-byte HDD bus!', - status: 'done', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-4424', - title: - 'Try to hack the HEX alarm, maybe it will connect the optical pixel!', - status: 'in progress', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-3922', - title: - "You can't back up the capacitor without generating the wireless PCI program!", - status: 'backlog', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-4921', - title: - "I'll index the open-source IP feed, that should system the GB application!", - status: 'canceled', - label: 'bug', - priority: 'low', - }, - { - id: 'TASK-5814', - title: 'We need to calculate the 1080p AGP feed!', - status: 'backlog', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-2645', - title: - "Synthesizing the system won't do anything, we need to navigate the multi-byte HDD firewall!", - status: 'todo', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-4535', - title: - 'Try to copy the JSON circuit, maybe it will connect the wireless feed!', - status: 'in progress', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-4463', - title: 'We need to copy the solid state AGP monitor!', - status: 'done', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-9745', - title: - 'If we connect the protocol, we can get to the GB system through the bluetooth PCI microchip!', - status: 'canceled', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-2080', - title: - 'If we input the bus, we can get to the RAM matrix through the auxiliary RAM card!', - status: 'todo', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-3838', - title: - "I'll bypass the online TCP application, that should panel the AGP system!", - status: 'backlog', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-1340', - title: 'We need to navigate the virtual PNG circuit!', - status: 'todo', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-6665', - title: - 'If we parse the monitor, we can get to the SSD hard drive through the cross-platform AGP alarm!', - status: 'canceled', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-7585', - title: - 'If we calculate the hard drive, we can get to the SSL program through the multi-byte CSS microchip!', - status: 'backlog', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-6319', - title: 'We need to copy the multi-byte SCSI program!', - status: 'backlog', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-4369', - title: 'Try to input the SCSI bus, maybe it will generate the 1080p pixel!', - status: 'backlog', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-9035', - title: 'We need to override the solid state PNG array!', - status: 'canceled', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-3970', - title: - "You can't index the transmitter without quantifying the haptic ASCII card!", - status: 'todo', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-4473', - title: - "You can't bypass the protocol without overriding the neural RSS program!", - status: 'todo', - label: 'documentation', - priority: 'low', - }, - { - id: 'TASK-4136', - title: - "You can't hack the hard drive without hacking the primary JSON program!", - status: 'canceled', - label: 'bug', - priority: 'medium', - }, - { - id: 'TASK-3939', - title: - 'Use the back-end SQL firewall, then you can connect the neural hard drive!', - status: 'done', - label: 'feature', - priority: 'low', - }, - { - id: 'TASK-2007', - title: - "I'll input the back-end USB protocol, that should bandwidth the PCI system!", - status: 'backlog', - label: 'bug', - priority: 'high', - }, - { - id: 'TASK-7516', - title: - 'Use the primary SQL program, then you can generate the auxiliary transmitter!', - status: 'done', - label: 'documentation', - priority: 'medium', - }, - { - id: 'TASK-6906', - title: - 'Try to back up the DRAM system, maybe it will reboot the online transmitter!', - status: 'done', - label: 'feature', - priority: 'high', - }, - { - id: 'TASK-5207', - title: - 'The SMS interface is down, copy the bluetooth bus so we can quantify the VGA card!', - status: 'in progress', - label: 'bug', - priority: 'low', - }, -] diff --git a/src/components/organisms/users-datatable/data/users.ts b/src/components/organisms/users-datatable/data/users.ts new file mode 100644 index 0000000..e1f95be --- /dev/null +++ b/src/components/organisms/users-datatable/data/users.ts @@ -0,0 +1,82 @@ +import { User } from "./schema"; +export const users: User[] = [ + { + id: 1, + email: 'alice@example.com', + name: 'Alice Johnson', + isAdmin: false, + createdAt: '2023-01-12T08:00:00Z', + orcid: '0000-0001-2345-6789', + }, + { + id: 2, + email: 'bob@example.com', + name: 'Bob Smith', + isAdmin: true, + createdAt: '2023-01-15T09:20:00Z', + orcid: '0000-0002-2345-6789', + }, + { + id: 3, + email: 'carol@example.com', + name: 'Carol White', + isAdmin: false, + createdAt: '2023-01-18T14:35:00Z', + orcid: '0000-0003-2345-6789', + }, + { + id: 4, + email: 'dave@example.com', + name: 'Dave Brown', + isAdmin: false, + createdAt: '2023-01-22T17:50:00Z', + orcid: '0000-0004-2345-6789', + }, + { + id: 5, + email: 'eve@example.com', + name: 'Eve Black', + isAdmin: true, + createdAt: '2023-01-28T21:15:00Z', + orcid: '0000-0005-2345-6789', + }, + { + id: 6, + email: 'frank@example.com', + name: 'Frank Green', + isAdmin: false, + createdAt: '2023-02-02T10:30:00Z', + orcid: '0000-0006-2345-6789', + }, + { + id: 7, + email: 'grace@example.com', + name: 'Grace Hall', + isAdmin: true, + createdAt: '2023-02-05T15:45:00Z', + orcid: '0000-0007-2345-6789', + }, + { + id: 8, + email: 'henry@example.com', + name: 'Henry Lee', + isAdmin: false, + createdAt: '2023-02-08T20:00:00Z', + orcid: '0000-0008-2345-6789', + }, + { + id: 9, + email: 'isla@example.com', + name: 'Isla Walker', + isAdmin: true, + createdAt: '2023-02-12T08:15:00Z', + orcid: '0000-0009-2345-6789', + }, + { + id: 10, + email: 'jake@example.com', + name: 'Jake King', + isAdmin: false, + createdAt: '2023-02-16T13:30:00Z', + orcid: '0000-0010-2345-6789', + }]; \ No newline at end of file diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 32d3a04..40e45b5 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -59,6 +59,13 @@ export interface CommunityAttestation { } export type ApiResponse = { data: T }; +export type PaginatedApiResponse = { + data: T; + count: number; + page: number; + cursor: number; +}; + type ApiError = { message: string }; export const listCommunitiesQuery = queryOptions({ @@ -75,7 +82,7 @@ export const listCommunitiesQuery = queryOptions({ }); export const attestationQueryOptions = (id: number) => { - console.log() + console.log(); return queryOptions({ queryKey: [{ type: tags.attestations, id }], queryFn: async () => { @@ -91,8 +98,8 @@ export const attestationQueryOptions = (id: number) => { >; return json.data ?? []; } catch (err) { - console.error({ err }) - return [] + console.error({ err }); + return []; } }, }); @@ -223,32 +230,6 @@ export const removeMember = async ({ }); }; -interface SearchResponse { - profiles: [ - { - name: string; - id: number; - orcid: string; //"0009-0000-3482-812X" - organisations: string[] - } - ]; -} -export async function searchUsers({ name }: { name?: string }) { - const response = await fetch( - `${NODES_API_URL}/v1/users/search?${name && "name=" + name}`, - { - credentials: "include", - mode: "cors", - } - ); - - const users = response.ok - ? ((await response.json()) as SearchResponse)?.profiles ?? [] - : []; - - return users; -} - export interface Analytics { newUsersInLast30Days: number; newUsersInLast7Days: number; @@ -276,7 +257,7 @@ export const getAnalytics = queryOptions({ const json = (await response.json()) as Analytics; return json || null; }, - staleTime: 60 * 1000 + staleTime: 60 * 1000, }); export const validateAuth = queryOptions({ @@ -289,3 +270,69 @@ export const validateAuth = queryOptions({ return json.ok || false; }, }); + +interface UserProfile { + id: number; + name: string; + email: string; + orcid: string; + isAdmin: boolean; + createdAt: string; +} + +export const searchUsers = queryOptions({ + queryKey: [tags.users], + queryFn: async (context) => { + console.log("context", context); + const response = await fetch(`${NODES_API_URL}/v1/admin/users/search`, { + credentials: "include", + }); + console.log("fetch users", response.ok, response.status); + const json = (await response.json()) as { + data: PaginatedApiResponse; + }; + return json; + }, +}); + + +export const toggleUserRole = async ({ + userId, +}: { + userId: number; +}) => { + return fetch( + `${NODES_API_URL}/v1/admin/users/${userId}/toggleRole`, + { + method: "PATCH", + credentials: "include", + } + ); +}; + +interface SearchResponse { + profiles: [ + { + name: string; + id: number; + orcid: string; //"0009-0000-3482-812X" + organisations: string[]; + } + ]; +} + +export async function searchUsersProfiles({ name }: { name?: string }) { + const response = await fetch( + `${NODES_API_URL}/v1/admin/users/search?${name ? "name=" + name : ""}`, + { + credentials: "include", + mode: "cors", + } + ); + + const users = response.ok + ? ((await response.json()) as SearchResponse)?.profiles ?? [] + : []; + + return users; +}