From 06b6bbefd4d29f02d7b6522ebf6e77b7d4c964b2 Mon Sep 17 00:00:00 2001 From: Lea Date: Thu, 18 Jan 2024 14:40:16 +0100 Subject: [PATCH] wip manage user dialog --- src/app/admin/users/page.tsx | 8 +- src/lib/actions.ts | 8 ++ .../ui/GenericConfirmationDialog.tsx | 32 +++++ .../components/ui/admin/ManageUserButton.tsx | 109 ++++++++++++++++++ src/lib/util.ts | 5 +- 5 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 src/lib/components/ui/GenericConfirmationDialog.tsx create mode 100644 src/lib/components/ui/admin/ManageUserButton.tsx diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index c23cf9f..c51bc2a 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -4,9 +4,10 @@ import { fetchAllUsers } from "@/lib/actions"; import GhostMessage from "@/lib/components/ui/GhostMessage"; import LoadingSpinner from "@/lib/components/ui/LoadingSpinner"; import CreateUserButton from "@/lib/components/ui/admin/CreateUserButton"; +import ManageUserButton from "@/lib/components/ui/admin/ManageUserButton"; import { GRAVATAR_DEFAULT } from "@/lib/constants"; -import { sha256sum } from "@/lib/util"; -import { Avatar, Button, Card, Flex, Heading, Table, Text, TextField } from "@radix-ui/themes"; +import { isAdmin, sha256sum } from "@/lib/util"; +import { Avatar, Badge, Button, Card, Flex, Heading, Table, Text, TextField } from "@radix-ui/themes"; import { SearchIcon, UserRoundXIcon } from "lucide-react"; import { useEffect, useMemo, useState } from "react"; @@ -63,10 +64,11 @@ export default function Users() { fallback={email.slice(0, 1) || "@"} /> {email} + {isAdmin(email) && Admin} - + ))} diff --git a/src/lib/actions.ts b/src/lib/actions.ts index 9111aca..18468dc 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -31,6 +31,14 @@ export async function fetchOwnAliases() { return await getUserAliases(session.user.email); } +export async function fetchUserAliases(email: string) { + const session = await getServerSession(); + if (!session?.user?.email) throw new Error("Unauthenticated"); + if (!isAdmin(session)) throw new Error("Unauthorized"); + + return await getUserAliases(email); +} + export async function fetchAllAliases() { const session = await getServerSession(); if (!session?.user?.email) throw new Error("Unauthenticated"); diff --git a/src/lib/components/ui/GenericConfirmationDialog.tsx b/src/lib/components/ui/GenericConfirmationDialog.tsx new file mode 100644 index 0000000..69eedf3 --- /dev/null +++ b/src/lib/components/ui/GenericConfirmationDialog.tsx @@ -0,0 +1,32 @@ +import { Button, Dialog, DialogDescription, DialogTitle, Flex } from "@radix-ui/themes"; + +export default function GenericConfirmationDialog({ + title, description, labelConfirm, action, children, +}: { + title: React.ReactNode, + description: React.ReactNode, + labelConfirm?: string, + action?: () => any, + children: React.ReactNode, +}) { + return ( + + + {children} + + + {title} + {description} + + + + + + + + + + + + ); +} diff --git a/src/lib/components/ui/admin/ManageUserButton.tsx b/src/lib/components/ui/admin/ManageUserButton.tsx new file mode 100644 index 0000000..10ac0f2 --- /dev/null +++ b/src/lib/components/ui/admin/ManageUserButton.tsx @@ -0,0 +1,109 @@ +"use client"; + +import { GRAVATAR_DEFAULT } from "@/lib/constants"; +import { AliasEntry } from "@/lib/db"; +import { isAdmin, sha256sum } from "@/lib/util"; +import { Avatar, Badge, Button, Card, Code, Dialog, Flex, Grid, Heading, Table, Text } from "@radix-ui/themes"; +import { useEffect, useState } from "react"; +import GhostMessage from "../GhostMessage"; +import { BananaIcon } from "lucide-react"; +import LoadingSpinner from "../LoadingSpinner"; +import { approveAlias, deleteAlias, fetchUserAliases } from "@/lib/actions"; +import GenericConfirmationDialog from "../GenericConfirmationDialog"; + +export default function ManageUserButton({ email }: { email: string }) { + const [aliases, setAliases] = useState(null); + + useEffect(() => { + fetchUserAliases(email).then(setAliases); + }, [email]); + + return ( + + + + + + + Manage user + + + + + + + + {email} + {isAdmin(email) ? "Administrator" : "User"} + + + + + + Aliases + + { + aliases + ? aliases.length > 0 + ? + + + Alias + Options + + + + + { + aliases.map((alias) => ( + + + {alias.alias} {alias.pending && Pending} + + + + { + alias.pending && + Do you want to approve {alias.address}'s alias request for {alias.alias}?} + labelConfirm="Approve" + action={async () => { + await approveAlias(alias.alias); + setAliases(aliases.map((a) => a.id == alias.id ? { ...alias, pending: false } : a)); + }} + > + + + } + Are you sure you want to delete {alias.alias}?} + labelConfirm="Delete" + action={async () => { + await deleteAlias(alias.alias); + setAliases(aliases.filter((a) => a.id != alias.id)); + }} + > + + + + + + )) + } + + + : } header="No aliases" message="The user does not have any aliases" /> + : } header="Loading" /> + } + + + + + ); +} diff --git a/src/lib/util.ts b/src/lib/util.ts index 2b4b443..43488c0 100644 --- a/src/lib/util.ts +++ b/src/lib/util.ts @@ -7,8 +7,9 @@ export function sha256sum(input: any) { return hash.digest('hex'); } -export function isAdmin(session: Session | null) { - return session?.user?.email == "lea@amogus.cloud"; // todo +export function isAdmin(session: Session | string | null) { + let email = typeof session == 'string' ? session : session?.user?.email; + return email == "lea@amogus.cloud"; // todo } export function aliasesNeedApproval(session: Session | null) {