diff --git a/src/app/admin/aliases/page.tsx b/src/app/admin/aliases/page.tsx new file mode 100644 index 0000000..8ad2e78 --- /dev/null +++ b/src/app/admin/aliases/page.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { fetchAllAliases } from "@/lib/actions"; +import GhostMessage from "@/lib/components/ui/GhostMessage"; +import LoadingSpinner from "@/lib/components/ui/LoadingSpinner"; +import { AliasEntry } from "@/lib/db"; +import { Button, Card, Flex, Heading, Table } from "@radix-ui/themes"; +import { ListChecksIcon } from "lucide-react"; +import { useEffect, useState } from "react"; + +export default function Aliases() { + const [aliases, setAliases] = useState(); + const pending = aliases?.filter((alias) => alias.pending); + + useEffect(() => { + fetchAllAliases().then(setAliases); + }, []); + + return ( + <> + Aliases + + + Pending + + {pending + ? pending.length + ? + + + User + Alias + Actions + + + + + {pending.map((alias) => ( + + {alias.address} + {alias.alias} + + + + + + + + ))} + + + : } header="All caught up" message="There are no pending alias requests" /> + : } header="Loading" /> + } + + + ); +} \ No newline at end of file diff --git a/src/lib/actions.ts b/src/lib/actions.ts index 8bf25b5..12dc48e 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -1,7 +1,7 @@ "use server"; import { getServerSession } from "next-auth"; -import { AliasEntry, createAlias, database, deleteAliasEntry, getAlias, getUserAliases, setUserPassword } from "./db"; +import { AliasEntry, createAlias, database, deleteAliasEntry, getAlias, getAllAliases, getUserAliases, setUserPassword } from "./db"; import { aliasesNeedApproval, isAdmin } from "./util"; export async function fetchAllUsers(): Promise { @@ -30,6 +30,14 @@ export async function fetchOwnAliases() { return await getUserAliases(session.user.email); } +export async function fetchAllAliases() { + const session = await getServerSession(); + if (!session?.user?.email) throw new Error("Unauthenticated"); + if (!isAdmin(session)) throw new Error("Unauthorized"); + + return await getAllAliases(); +} + export async function aliasAvailable(email: string) { const session = await getServerSession(); if (!session?.user) throw new Error("Unauthenticated"); diff --git a/src/lib/components/ui/NavigationPanel.tsx b/src/lib/components/ui/NavigationPanel.tsx index 9152a5d..7e01d56 100644 --- a/src/lib/components/ui/NavigationPanel.tsx +++ b/src/lib/components/ui/NavigationPanel.tsx @@ -1,8 +1,8 @@ "use client"; import { isAdmin } from "@/lib/util"; -import { Avatar, Box, Button, Card, Flex, IconButton, Popover, ScrollArea, Text } from "@radix-ui/themes"; -import { BookUserIcon, HomeIcon } from "lucide-react"; +import { Avatar, Button, Card, Flex, IconButton, Popover, ScrollArea, Text, Tooltip } from "@radix-ui/themes"; +import { BookUserIcon, HomeIcon, Users2Icon } from "lucide-react"; import { signOut, useSession } from "next-auth/react"; import Link from "next/link"; import dayjs from "dayjs"; @@ -12,6 +12,7 @@ dayjs.extend(relativeTime); export default function NavigationPanel({ mobileUi }: { mobileUi?: boolean }) { const session = useSession(); + const tooltipSide = mobileUi ? "bottom" : "right"; return ( @@ -58,18 +59,31 @@ export default function NavigationPanel({ mobileUi }: { mobileUi?: boolean }) { )} - - - - - - - {isAdmin(session.data) && ( - + + - + + + + {isAdmin(session.data) && ( + <> + + + + + + + + + + + + + + + )} diff --git a/src/lib/db.ts b/src/lib/db.ts index e20c0e2..57015d4 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -62,6 +62,20 @@ export function setUserPassword(email: string, newPass: string) { } export type AliasEntry = { id: number, address: string, alias: string, pending: boolean }; +export function getAllAliases() { + return new Promise(async (resolve, reject) => { + const db = database('aliases'); + + db.all("SELECT id, address, alias, pending FROM aliases", (err, res: any[]) => { + db.close(); + if (err) return reject(err); + resolve(res.map((data) => ({ + ...data, + pending: !!data.pending, + }))); + }); + }); +} export function getUserAliases(email: string) { return new Promise(async (resolve, reject) => { const db = database('aliases');