From 76c628d6d875fb0ee179efc0422e3ea331ef3545 Mon Sep 17 00:00:00 2001 From: Aaron Sutula Date: Thu, 11 Jul 2024 11:53:52 -0600 Subject: [PATCH] acl display Signed-off-by: Aaron Sutula --- packages/api/src/routers/users.ts | 9 +- packages/web/components/acl.tsx | 120 ++++++++++++++++++++++ packages/web/components/console.tsx | 4 +- packages/web/components/data-table.tsx | 18 +--- packages/web/components/hash-display.tsx | 17 +-- packages/web/components/table-data.tsx | 3 +- packages/web/components/table-details.tsx | 11 +- packages/web/components/table.tsx | 16 ++- 8 files changed, 156 insertions(+), 42 deletions(-) create mode 100644 packages/web/components/acl.tsx diff --git a/packages/api/src/routers/users.ts b/packages/api/src/routers/users.ts index f0146706..f6caa9e3 100644 --- a/packages/api/src/routers/users.ts +++ b/packages/api/src/routers/users.ts @@ -8,7 +8,14 @@ export function usersRouter(store: Store) { .input(z.object({ addresses: z.array(z.string().trim()).min(1) })) .query(async ({ input }) => { const users = await store.users.usersForAddresses(input.addresses); - return users; + const res = users.reduce>( + (acc, item) => { + acc[item.user.address] = item; + return acc; + }, + {}, + ); + return res; }), }); } diff --git a/packages/web/components/acl.tsx b/packages/web/components/acl.tsx new file mode 100644 index 00000000..b43f3b26 --- /dev/null +++ b/packages/web/components/acl.tsx @@ -0,0 +1,120 @@ +import { + type ColumnDef, + getCoreRowModel, + useReactTable, + type Cell, +} from "@tanstack/react-table"; +import { type RouterOutputs } from "@tableland/studio-api"; +import { useEffect, useState } from "react"; +import { Check } from "lucide-react"; +import Link from "next/link"; +import { DataTable } from "./data-table"; +import HashDisplay from "./hash-display"; +import { type ACLItem } from "@/lib/validator-queries"; + +type TableRowData = ACLItem & { + isOwner: boolean; +} & RouterOutputs["users"]["usersForAddresses"][string]; + +interface Props { + acl: ACLItem[]; + authorizedStudioUsers: RouterOutputs["users"]["usersForAddresses"]; + owner: string; +} + +export default function ACL({ acl, authorizedStudioUsers, owner }: Props) { + const data: TableRowData[] = acl.map((item) => { + const studioUser = authorizedStudioUsers[item.controller]; + const isOwner = item.controller === owner; + return { + ...item, + ...studioUser, + isOwner, + }; + }); + + const columns: Array> = [ + { + accessorKey: "controller", + header: "Address", + cell: AddressCell, + }, + { + accessorKey: "team.name", + header: "Studio User", + cell: UserCell, + }, + { + accessorKey: "isOwner", + header: "Table Owner", + cell: BoolCell, + }, + { + accessorKey: "privileges.insert", + header: "Insert", + cell: BoolCell, + }, + { + accessorKey: "privileges.update", + header: "Update", + cell: BoolCell, + }, + { + accessorKey: "privileges.delete", + header: "Delete", + cell: BoolCell, + }, + ]; + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + }); + return ; +} + +function AddressCell({ + getValue, +}: ReturnType["getContext"]>) { + const initialValue = getValue(); + + const [value, setValue] = useState(initialValue); + + useEffect(() => { + setValue(initialValue); + }, [initialValue]); + + return ( + + ); +} + +function BoolCell({ + getValue, +}: ReturnType["getContext"]>) { + const initialValue = getValue(); + + const [value, setValue] = useState(initialValue); + + useEffect(() => { + setValue(initialValue); + }, [initialValue]); + + return value ? : null; +} + +function UserCell({ + getValue, + row, +}: ReturnType["getContext"]>) { + const initialValue = getValue(); + + const [value, setValue] = useState(initialValue); + + useEffect(() => { + setValue(initialValue); + }, [initialValue]); + + return {value}; +} diff --git a/packages/web/components/console.tsx b/packages/web/components/console.tsx index 93149884..1165e26b 100644 --- a/packages/web/components/console.tsx +++ b/packages/web/components/console.tsx @@ -386,9 +386,7 @@ function ResultSetPane(props: any): React.JSX.Element { })} )} - {!tab.error && !tab.messages?.length && ( - - )} + {!tab.error && !tab.messages?.length && } ); } diff --git a/packages/web/components/data-table.tsx b/packages/web/components/data-table.tsx index 834f4e60..279e9512 100644 --- a/packages/web/components/data-table.tsx +++ b/packages/web/components/data-table.tsx @@ -1,8 +1,4 @@ -import { - type ColumnDef, - flexRender, - type Table as TSTable, -} from "@tanstack/react-table"; +import { flexRender, type Table as TSTable } from "@tanstack/react-table"; import React from "react"; import { Button } from "@/components/ui/button"; import { @@ -14,17 +10,11 @@ import { TableRow, } from "@/components/ui/table"; -interface DataTableProps { - columns: Array>; - data: TData[]; +interface DataTableProps { table: TSTable; } -export function DataTable({ - columns, - data, - table, -}: DataTableProps) { +export function DataTable({ table }: DataTableProps) { return (
@@ -68,7 +58,7 @@ export function DataTable({ ) : ( No results. diff --git a/packages/web/components/hash-display.tsx b/packages/web/components/hash-display.tsx index 20008f32..742bbdd5 100644 --- a/packages/web/components/hash-display.tsx +++ b/packages/web/components/hash-display.tsx @@ -19,7 +19,7 @@ export default function HashDisplay({ hashDesc = "address", className, ...rest -}: HTMLProps & { +}: HTMLProps & { hash: string; numCharacters?: number; copy?: boolean; @@ -32,16 +32,17 @@ export default function HashDisplay({ : hash; return ( -
+
- - {slicedHash} - + {slicedHash}

{hash}

diff --git a/packages/web/components/table-data.tsx b/packages/web/components/table-data.tsx index 7753659f..dfb9dc9d 100644 --- a/packages/web/components/table-data.tsx +++ b/packages/web/components/table-data.tsx @@ -421,8 +421,7 @@ export function TableData({ )}
- -
{JSON.stringify(updates, null, "\t")}
+ ); } diff --git a/packages/web/components/table-details.tsx b/packages/web/components/table-details.tsx index adb54749..50788ead 100644 --- a/packages/web/components/table-details.tsx +++ b/packages/web/components/table-details.tsx @@ -9,6 +9,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs"; import SQLLogs from "./sql-logs"; import { TableData } from "./table-data"; import HashDisplay from "./hash-display"; +import ACL from "./acl"; import { cn } from "@/lib/utils"; import DefDetails from "@/components/def-details"; import { type TablePermissions } from "@/lib/validator-queries"; @@ -55,9 +56,7 @@ export default function TableDetails({ const authorizedStudioUser = useMemo( () => - authorizedStudioUsers?.find( - (item) => item.user.address === addressPostMount, - ), + addressPostMount ? authorizedStudioUsers[addressPostMount] : undefined, [addressPostMount, authorizedStudioUsers], ); @@ -168,7 +167,11 @@ export default function TableDetails({ -
{JSON.stringify(tablePermissions, null, 2)}
+
); diff --git a/packages/web/components/table.tsx b/packages/web/components/table.tsx index 1488357f..9d865baf 100644 --- a/packages/web/components/table.tsx +++ b/packages/web/components/table.tsx @@ -87,9 +87,7 @@ export default async function Table({ const openSeaLink = openSeaLinks.get(chainId); let tablePermissions: TablePermissions | undefined; - let authorizedStudioUsers: - | RouterOutputs["users"]["usersForAddresses"] - | undefined; + let authorizedStudioUsers: RouterOutputs["users"]["usersForAddresses"] = {}; let data: Result> | undefined; let error: Error | undefined; try { @@ -100,7 +98,7 @@ export default async function Table({ ? await api.users.usersForAddresses({ addresses: authorizedAddresses, }) - : []; + : {}; const tbl = new Database({ baseUrl }); data = await tbl.prepare(`SELECT * FROM ${tableName};`).all(); } catch (err) { @@ -111,9 +109,7 @@ export default async function Table({ await api.deployments.deploymentReferences({ chainId, tableId }) ).filter((p) => p.environment.id !== environment?.id); - const authorizedStudioUser = authorizedStudioUsers?.find( - (item) => item.user.address === owner, - ); + const ownerStudioUser = owner ? authorizedStudioUsers[owner] : undefined; const displayName = defData?.name ?? tableName; @@ -197,10 +193,10 @@ export default async function Table({ - {authorizedStudioUser && ( + {ownerStudioUser && ( - Studio user {authorizedStudioUser.team.name} - {authorizedStudioUser.user.teamId === session.auth?.user.teamId + Studio user {ownerStudioUser.team.name} + {ownerStudioUser.user.teamId === session.auth?.user.teamId ? " (you)" : ""}