Skip to content

Commit

Permalink
acl display
Browse files Browse the repository at this point in the history
Signed-off-by: Aaron Sutula <asutula@users.noreply.github.com>
  • Loading branch information
asutula committed Jul 11, 2024
1 parent f3c991d commit 76c628d
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 42 deletions.
9 changes: 8 additions & 1 deletion packages/api/src/routers/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Record<string, (typeof users)[number]>>(
(acc, item) => {
acc[item.user.address] = item;
return acc;
},
{},
);
return res;
}),
});
}
120 changes: 120 additions & 0 deletions packages/web/components/acl.tsx
Original file line number Diff line number Diff line change
@@ -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<ColumnDef<TableRowData>> = [
{
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 <DataTable table={table} />;
}

function AddressCell({
getValue,
}: ReturnType<Cell<TableRowData, unknown>["getContext"]>) {
const initialValue = getValue<string>();

const [value, setValue] = useState(initialValue);

useEffect(() => {
setValue(initialValue);
}, [initialValue]);

return (
<HashDisplay hash={value} copy className="justify-start text-foreground" />
);
}

function BoolCell({
getValue,
}: ReturnType<Cell<TableRowData, unknown>["getContext"]>) {
const initialValue = getValue<boolean>();

const [value, setValue] = useState(initialValue);

useEffect(() => {
setValue(initialValue);
}, [initialValue]);

return value ? <Check /> : null;
}

function UserCell({
getValue,
row,
}: ReturnType<Cell<TableRowData, unknown>["getContext"]>) {
const initialValue = getValue<string>();

const [value, setValue] = useState(initialValue);

useEffect(() => {
setValue(initialValue);
}, [initialValue]);

return <Link href={`/${row.original.team.slug}`}>{value}</Link>;
}
4 changes: 1 addition & 3 deletions packages/web/components/console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,7 @@ function ResultSetPane(props: any): React.JSX.Element {
})}
</div>
)}
{!tab.error && !tab.messages?.length && (
<DataTable columns={tab.columns} data={data} table={table} />
)}
{!tab.error && !tab.messages?.length && <DataTable table={table} />}
</div>
);
}
18 changes: 4 additions & 14 deletions packages/web/components/data-table.tsx
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -14,17 +10,11 @@ import {
TableRow,
} from "@/components/ui/table";

interface DataTableProps<TData, TValue> {
columns: Array<ColumnDef<TData, TValue>>;
data: TData[];
interface DataTableProps<TData> {
table: TSTable<TData>;
}

export function DataTable<TData, TValue>({
columns,
data,
table,
}: DataTableProps<TData, TValue>) {
export function DataTable<TData>({ table }: DataTableProps<TData>) {
return (
<div>
<div className="mt-4 rounded-md border">
Expand Down Expand Up @@ -68,7 +58,7 @@ export function DataTable<TData, TValue>({
) : (
<TableRow>
<TableCell
colSpan={columns.length}
colSpan={table.getAllColumns().length}
className="h-24 text-center"
>
No results.
Expand Down
17 changes: 9 additions & 8 deletions packages/web/components/hash-display.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function HashDisplay({
hashDesc = "address",
className,
...rest
}: HTMLProps<HTMLSpanElement> & {
}: HTMLProps<HTMLDivElement> & {
hash: string;
numCharacters?: number;
copy?: boolean;
Expand All @@ -32,16 +32,17 @@ export default function HashDisplay({
: hash;

return (
<div className="flex items-center justify-center">
<div
className={cn(
"flex items-center justify-center text-sm text-muted-foreground",
className,
)}
{...rest}
>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<span
className={cn("text-sm text-muted-foreground", className)}
{...rest}
>
{slicedHash}
</span>
<span {...rest}>{slicedHash}</span>
</TooltipTrigger>
<TooltipContent>
<p>{hash}</p>
Expand Down
3 changes: 1 addition & 2 deletions packages/web/components/table-data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,7 @@ export function TableData({
</DropdownMenu>
)}
</div>
<DataTable columns={columns} data={data} table={table} />
<pre>{JSON.stringify(updates, null, "\t")}</pre>
<DataTable table={table} />
</>
);
}
11 changes: 7 additions & 4 deletions packages/web/components/table-details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -55,9 +56,7 @@ export default function TableDetails({

const authorizedStudioUser = useMemo(
() =>
authorizedStudioUsers?.find(
(item) => item.user.address === addressPostMount,
),
addressPostMount ? authorizedStudioUsers[addressPostMount] : undefined,
[addressPostMount, authorizedStudioUsers],
);

Expand Down Expand Up @@ -168,7 +167,11 @@ export default function TableDetails({
<DefDetails name={displayName} schema={schema} />
</TabsContent>
<TabsContent value="permissions" className="space-y-4">
<pre>{JSON.stringify(tablePermissions, null, 2)}</pre>
<ACL
acl={Object.values(tablePermissions)}
authorizedStudioUsers={authorizedStudioUsers}
owner={owner}
/>
</TabsContent>
</Tabs>
);
Expand Down
16 changes: 6 additions & 10 deletions packages/web/components/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<Record<string, unknown>> | undefined;
let error: Error | undefined;
try {
Expand All @@ -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) {
Expand All @@ -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;

Expand Down Expand Up @@ -197,10 +193,10 @@ export default async function Table({
<MetricCardContent>
<HashDisplay className="text-3xl text-foreground" hash={owner} />
</MetricCardContent>
{authorizedStudioUser && (
{ownerStudioUser && (
<MetricCardFooter>
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)"
: ""}
</MetricCardFooter>
Expand Down

0 comments on commit 76c628d

Please sign in to comment.