diff options
| author | MohamedBassem <me@mbassem.com> | 2024-03-13 21:43:44 +0000 |
|---|---|---|
| committer | Mohamed Bassem <me@mbassem.com> | 2024-03-14 16:40:45 +0000 |
| commit | 04572a8e5081b1e4871e273cde9dbaaa44c52fe0 (patch) | |
| tree | 8e993acb732a50d1306d4d6953df96c165c57f57 /packages/web/app/dashboard | |
| parent | 2df08ed08c065e8b91bc8df0266bd4bcbb062be4 (diff) | |
| download | karakeep-04572a8e5081b1e4871e273cde9dbaaa44c52fe0.tar.zst | |
structure: Create apps dir and copy tooling dir from t3-turbo repo
Diffstat (limited to 'packages/web/app/dashboard')
| -rw-r--r-- | packages/web/app/dashboard/admin/page.tsx | 203 | ||||
| -rw-r--r-- | packages/web/app/dashboard/archive/page.tsx | 9 | ||||
| -rw-r--r-- | packages/web/app/dashboard/bookmarks/layout.tsx | 23 | ||||
| -rw-r--r-- | packages/web/app/dashboard/bookmarks/loading.tsx | 11 | ||||
| -rw-r--r-- | packages/web/app/dashboard/bookmarks/page.tsx | 5 | ||||
| -rw-r--r-- | packages/web/app/dashboard/error.tsx | 9 | ||||
| -rw-r--r-- | packages/web/app/dashboard/favourites/page.tsx | 14 | ||||
| -rw-r--r-- | packages/web/app/dashboard/layout.tsx | 24 | ||||
| -rw-r--r-- | packages/web/app/dashboard/lists/[listId]/page.tsx | 44 | ||||
| -rw-r--r-- | packages/web/app/dashboard/lists/page.tsx | 14 | ||||
| -rw-r--r-- | packages/web/app/dashboard/not-found.tsx | 7 | ||||
| -rw-r--r-- | packages/web/app/dashboard/preview/[bookmarkId]/page.tsx | 14 | ||||
| -rw-r--r-- | packages/web/app/dashboard/search/page.tsx | 41 | ||||
| -rw-r--r-- | packages/web/app/dashboard/settings/page.tsx | 9 | ||||
| -rw-r--r-- | packages/web/app/dashboard/tags/[tagName]/page.tsx | 55 | ||||
| -rw-r--r-- | packages/web/app/dashboard/tags/page.tsx | 56 |
16 files changed, 0 insertions, 538 deletions
diff --git a/packages/web/app/dashboard/admin/page.tsx b/packages/web/app/dashboard/admin/page.tsx deleted file mode 100644 index 6babdd79..00000000 --- a/packages/web/app/dashboard/admin/page.tsx +++ /dev/null @@ -1,203 +0,0 @@ -"use client"; - -import { ActionButton } from "@/components/ui/action-button"; -import LoadingSpinner from "@/components/ui/spinner"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { toast } from "@/components/ui/use-toast"; -import { api } from "@/lib/trpc"; -import { keepPreviousData } from "@tanstack/react-query"; -import { Trash } from "lucide-react"; -import { useSession } from "next-auth/react"; -import { useRouter } from "next/navigation"; - -function ActionsSection() { - const { mutate: recrawlLinks, isPending: isRecrawlPending } = - api.admin.recrawlAllLinks.useMutation({ - onSuccess: () => { - toast({ - description: "Recrawl enqueued", - }); - }, - onError: (e) => { - toast({ - variant: "destructive", - description: e.message, - }); - }, - }); - - const { mutate: reindexBookmarks, isPending: isReindexPending } = - api.admin.reindexAllBookmarks.useMutation({ - onSuccess: () => { - toast({ - description: "Reindex enqueued", - }); - }, - onError: (e) => { - toast({ - variant: "destructive", - description: e.message, - }); - }, - }); - - return ( - <> - <p className="text-xl">Actions</p> - <ActionButton - className="w-1/2" - variant="destructive" - loading={isRecrawlPending} - onClick={() => recrawlLinks()} - > - Recrawl All Links - </ActionButton> - <ActionButton - className="w-1/2" - variant="destructive" - loading={isReindexPending} - onClick={() => reindexBookmarks()} - > - Reindex All Bookmarks - </ActionButton> - </> - ); -} - -function ServerStatsSection() { - const { data: serverStats } = api.admin.stats.useQuery(undefined, { - refetchInterval: 1000, - placeholderData: keepPreviousData, - }); - - if (!serverStats) { - return <LoadingSpinner />; - } - - return ( - <> - <p className="text-xl">Server Stats</p> - <Table className="w-1/2"> - <TableBody> - <TableRow> - <TableCell className="w-2/3">Num Users</TableCell> - <TableCell>{serverStats.numUsers}</TableCell> - </TableRow> - <TableRow> - <TableCell>Num Bookmarks</TableCell> - <TableCell>{serverStats.numBookmarks}</TableCell> - </TableRow> - </TableBody> - </Table> - <hr /> - <p className="text-xl">Background Jobs</p> - <Table className="w-1/2"> - <TableBody> - <TableRow> - <TableCell className="w-2/3">Pending Crawling Jobs</TableCell> - <TableCell>{serverStats.pendingCrawls}</TableCell> - </TableRow> - <TableRow> - <TableCell>Pending Indexing Jobs</TableCell> - <TableCell>{serverStats.pendingIndexing}</TableCell> - </TableRow> - <TableRow> - <TableCell>Pending OpenAI Jobs</TableCell> - <TableCell>{serverStats.pendingOpenai}</TableCell> - </TableRow> - </TableBody> - </Table> - </> - ); -} - -function UsersSection() { - const { data: session } = useSession(); - const invalidateUserList = api.useUtils().users.list.invalidate; - const { data: users } = api.users.list.useQuery(); - const { mutate: deleteUser, isPending: isDeletionPending } = - api.users.delete.useMutation({ - onSuccess: () => { - toast({ - description: "User deleted", - }); - invalidateUserList(); - }, - onError: (e) => { - toast({ - variant: "destructive", - description: `Something went wrong: ${e.message}`, - }); - }, - }); - - if (!users) { - return <LoadingSpinner />; - } - - return ( - <> - <p className="text-xl">Users</p> - <Table> - <TableHeader> - <TableHead>Name</TableHead> - <TableHead>Email</TableHead> - <TableHead>Role</TableHead> - <TableHead>Action</TableHead> - </TableHeader> - <TableBody> - {users.users.map((u) => ( - <TableRow key={u.id}> - <TableCell>{u.name}</TableCell> - <TableCell>{u.email}</TableCell> - <TableCell>{u.role}</TableCell> - <TableCell> - <ActionButton - variant="destructive" - onClick={() => deleteUser({ userId: u.id })} - loading={isDeletionPending} - disabled={session!.user.id == u.id} - > - <Trash /> - </ActionButton> - </TableCell> - </TableRow> - ))} - </TableBody> - </Table> - </> - ); -} - -export default function AdminPage() { - const router = useRouter(); - const { data: session, status } = useSession(); - - if (status == "loading") { - return <LoadingSpinner />; - } - - if (!session || session.user.role != "admin") { - router.push("/"); - return; - } - - return ( - <div className="m-4 flex flex-col gap-5 rounded-md border bg-white p-4"> - <p className="text-2xl">Admin</p> - <hr /> - <ServerStatsSection /> - <hr /> - <UsersSection /> - <hr /> - <ActionsSection /> - </div> - ); -} diff --git a/packages/web/app/dashboard/archive/page.tsx b/packages/web/app/dashboard/archive/page.tsx deleted file mode 100644 index 69559185..00000000 --- a/packages/web/app/dashboard/archive/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks"; - -export default async function ArchivedBookmarkPage() { - return ( - <div className="continer mt-4"> - <Bookmarks title="🗄️ Archive" archived={true} showDivider={true} /> - </div> - ); -} diff --git a/packages/web/app/dashboard/bookmarks/layout.tsx b/packages/web/app/dashboard/bookmarks/layout.tsx deleted file mode 100644 index 71ee143b..00000000 --- a/packages/web/app/dashboard/bookmarks/layout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from "react"; -import TopNav from "@/components/dashboard/bookmarks/TopNav"; -import type { Metadata } from "next"; - -export const metadata: Metadata = { - title: "Hoarder - Bookmarks", -}; - -export default function BookmarksLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - <div className="flex h-full flex-col"> - <div> - <TopNav /> - </div> - <hr /> - <div className="my-4 flex-1 pb-4">{children}</div> - </div> - ); -} diff --git a/packages/web/app/dashboard/bookmarks/loading.tsx b/packages/web/app/dashboard/bookmarks/loading.tsx deleted file mode 100644 index 4e56c3c4..00000000 --- a/packages/web/app/dashboard/bookmarks/loading.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import Spinner from "@/components/ui/spinner"; - -export default function Loading() { - return ( - <div className="flex size-full"> - <div className="m-auto"> - <Spinner /> - </div> - </div> - ); -} diff --git a/packages/web/app/dashboard/bookmarks/page.tsx b/packages/web/app/dashboard/bookmarks/page.tsx deleted file mode 100644 index c9391d85..00000000 --- a/packages/web/app/dashboard/bookmarks/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks"; - -export default async function BookmarksPage() { - return <Bookmarks title="Bookmarks" archived={false} />; -} diff --git a/packages/web/app/dashboard/error.tsx b/packages/web/app/dashboard/error.tsx deleted file mode 100644 index 556e59a3..00000000 --- a/packages/web/app/dashboard/error.tsx +++ /dev/null @@ -1,9 +0,0 @@ -"use client"; - -export default function Error() { - return ( - <div className="flex size-full"> - <div className="m-auto text-3xl">Something went wrong</div> - </div> - ); -} diff --git a/packages/web/app/dashboard/favourites/page.tsx b/packages/web/app/dashboard/favourites/page.tsx deleted file mode 100644 index de17461d..00000000 --- a/packages/web/app/dashboard/favourites/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import Bookmarks from "@/components/dashboard/bookmarks/Bookmarks"; - -export default async function FavouritesBookmarkPage() { - return ( - <div className="continer mt-4"> - <Bookmarks - title="⭐️ Favourites" - archived={false} - favourited={true} - showDivider={true} - /> - </div> - ); -} diff --git a/packages/web/app/dashboard/layout.tsx b/packages/web/app/dashboard/layout.tsx deleted file mode 100644 index 31d592fb..00000000 --- a/packages/web/app/dashboard/layout.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import { Separator } from "@/components/ui/separator"; -import MobileSidebar from "@/components/dashboard/sidebar/ModileSidebar"; -import Sidebar from "@/components/dashboard/sidebar/Sidebar"; - -export default async function Dashboard({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - <div className="flex min-h-screen w-screen flex-col sm:h-screen sm:flex-row"> - <div className="hidden flex-none sm:flex"> - <Sidebar /> - </div> - <main className="flex-1 bg-gray-100 sm:overflow-y-auto"> - <div className="block w-full sm:hidden"> - <MobileSidebar /> - <Separator /> - </div> - {children} - </main> - </div> - ); -} diff --git a/packages/web/app/dashboard/lists/[listId]/page.tsx b/packages/web/app/dashboard/lists/[listId]/page.tsx deleted file mode 100644 index 006fd3ad..00000000 --- a/packages/web/app/dashboard/lists/[listId]/page.tsx +++ /dev/null @@ -1,44 +0,0 @@ -import { api } from "@/server/api/client"; -import { getServerAuthSession } from "@/server/auth"; -import { TRPCError } from "@trpc/server"; -import { notFound, redirect } from "next/navigation"; -import ListView from "@/components/dashboard/lists/ListView"; -import DeleteListButton from "@/components/dashboard/lists/DeleteListButton"; - -export default async function ListPage({ - params, -}: { - params: { listId: string }; -}) { - const session = await getServerAuthSession(); - if (!session) { - redirect("/"); - } - - let list; - try { - list = await api.lists.get({ listId: params.listId }); - } catch (e) { - if (e instanceof TRPCError) { - if (e.code == "NOT_FOUND") { - notFound(); - } - } - throw e; - } - - const bookmarks = await api.bookmarks.getBookmarks({ ids: list.bookmarks }); - - return ( - <div className="container flex flex-col gap-3"> - <div className="flex justify-between"> - <span className="pt-4 text-2xl"> - {list.icon} {list.name} - </span> - <DeleteListButton list={list} /> - </div> - <hr /> - <ListView list={list} bookmarks={bookmarks.bookmarks} /> - </div> - ); -} diff --git a/packages/web/app/dashboard/lists/page.tsx b/packages/web/app/dashboard/lists/page.tsx deleted file mode 100644 index 88eeda47..00000000 --- a/packages/web/app/dashboard/lists/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { api } from "@/server/api/client"; -import AllListsView from "@/components/dashboard/lists/AllListsView"; - -export default async function ListsPage() { - const lists = await api.lists.list(); - - return ( - <div className="container mt-4 flex flex-col gap-3"> - <p className="text-2xl">📋 All Lists</p> - <hr /> - <AllListsView initialData={lists.lists} /> - </div> - ); -} diff --git a/packages/web/app/dashboard/not-found.tsx b/packages/web/app/dashboard/not-found.tsx deleted file mode 100644 index 64df220c..00000000 --- a/packages/web/app/dashboard/not-found.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function NotFound() { - return ( - <div className="flex size-full"> - <div className="m-auto text-3xl">Not Found :(</div> - </div> - ); -} diff --git a/packages/web/app/dashboard/preview/[bookmarkId]/page.tsx b/packages/web/app/dashboard/preview/[bookmarkId]/page.tsx deleted file mode 100644 index 707d2b69..00000000 --- a/packages/web/app/dashboard/preview/[bookmarkId]/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { api } from "@/server/api/client"; -import BookmarkPreview from "@/components/dashboard/bookmarks/BookmarkPreview"; - -export default async function BookmarkPreviewPage({ - params, -}: { - params: { bookmarkId: string }; -}) { - const bookmark = await api.bookmarks.getBookmark({ - bookmarkId: params.bookmarkId, - }); - - return <BookmarkPreview initialData={bookmark} />; -} diff --git a/packages/web/app/dashboard/search/page.tsx b/packages/web/app/dashboard/search/page.tsx deleted file mode 100644 index 602f6aa0..00000000 --- a/packages/web/app/dashboard/search/page.tsx +++ /dev/null @@ -1,41 +0,0 @@ -"use client"; - -import BookmarksGrid from "@/components/dashboard/bookmarks/BookmarksGrid"; -import Loading from "../bookmarks/loading"; -import { Suspense, useRef } from "react"; -import { SearchInput } from "@/components/dashboard/search/SearchInput"; -import { useBookmarkSearch } from "@/lib/hooks/bookmark-search"; - -function SearchComp() { - const { data, isPending, isPlaceholderData } = useBookmarkSearch(); - - const inputRef: React.MutableRefObject<HTMLInputElement | null> = - useRef<HTMLInputElement | null>(null); - - return ( - <div className="container flex flex-col gap-3 p-4"> - <SearchInput - ref={inputRef} - autoFocus={true} - loading={isPending || isPlaceholderData} - /> - <hr /> - {data ? ( - <BookmarksGrid - query={{ ids: data.bookmarks.map((b) => b.id) }} - bookmarks={data.bookmarks} - /> - ) : ( - <Loading /> - )} - </div> - ); -} - -export default function SearchPage() { - return ( - <Suspense> - <SearchComp /> - </Suspense> - ); -} diff --git a/packages/web/app/dashboard/settings/page.tsx b/packages/web/app/dashboard/settings/page.tsx deleted file mode 100644 index 38091e6c..00000000 --- a/packages/web/app/dashboard/settings/page.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import ApiKeySettings from "@/components/dashboard/settings/ApiKeySettings"; -export default async function Settings() { - return ( - <div className="m-4 flex flex-col space-y-2 rounded-md border bg-white p-4"> - <p className="text-2xl">Settings</p> - <ApiKeySettings /> - </div> - ); -} diff --git a/packages/web/app/dashboard/tags/[tagName]/page.tsx b/packages/web/app/dashboard/tags/[tagName]/page.tsx deleted file mode 100644 index c978b86a..00000000 --- a/packages/web/app/dashboard/tags/[tagName]/page.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { getServerAuthSession } from "@/server/auth"; -import { db } from "@hoarder/db"; -import { notFound, redirect } from "next/navigation"; -import BookmarksGrid from "@/components/dashboard/bookmarks/BookmarksGrid"; -import { api } from "@/server/api/client"; -import { bookmarkTags, tagsOnBookmarks } from "@hoarder/db/schema"; -import { and, eq } from "drizzle-orm"; - -export default async function TagPage({ - params, -}: { - params: { tagName: string }; -}) { - const session = await getServerAuthSession(); - if (!session) { - redirect("/"); - } - const tagName = decodeURIComponent(params.tagName); - const tag = await db.query.bookmarkTags.findFirst({ - where: and( - eq(bookmarkTags.userId, session.user.id), - eq(bookmarkTags.name, tagName), - ), - columns: { - id: true, - }, - }); - - if (!tag) { - // TODO: Better error message when the tag is not there - notFound(); - } - - const bookmarkIds = await db.query.tagsOnBookmarks.findMany({ - where: eq(tagsOnBookmarks.tagId, tag.id), - columns: { - bookmarkId: true, - }, - }); - - const query = { - ids: bookmarkIds.map((b) => b.bookmarkId), - archived: false, - }; - - const bookmarks = await api.bookmarks.getBookmarks(query); - - return ( - <div className="container flex flex-col gap-3"> - <span className="pt-4 text-2xl">{tagName}</span> - <hr /> - <BookmarksGrid query={query} bookmarks={bookmarks.bookmarks} /> - </div> - ); -} diff --git a/packages/web/app/dashboard/tags/page.tsx b/packages/web/app/dashboard/tags/page.tsx deleted file mode 100644 index 44c164e1..00000000 --- a/packages/web/app/dashboard/tags/page.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Separator } from "@/components/ui/separator"; -import { getServerAuthSession } from "@/server/auth"; -import { db } from "@hoarder/db"; -import { bookmarkTags, tagsOnBookmarks } from "@hoarder/db/schema"; -import { count, eq } from "drizzle-orm"; -import Link from "next/link"; -import { redirect } from "next/navigation"; - -function TagPill({ name, count }: { name: string; count: number }) { - return ( - <Link - className="text-foreground hover:bg-foreground hover:text-background flex gap-2 rounded-md border border-gray-200 bg-white px-2 py-1" - href={`/dashboard/tags/${name}`} - > - {name} <Separator orientation="vertical" /> {count} - </Link> - ); -} - -export default async function TagsPage() { - const session = await getServerAuthSession(); - if (!session) { - redirect("/"); - } - - let tags = await db - .select({ - id: tagsOnBookmarks.tagId, - name: bookmarkTags.name, - count: count(), - }) - .from(tagsOnBookmarks) - .where(eq(bookmarkTags.userId, session.user.id)) - .groupBy(tagsOnBookmarks.tagId) - .innerJoin(bookmarkTags, eq(bookmarkTags.id, tagsOnBookmarks.tagId)); - - // Sort tags by usage desc - tags = tags.sort((a, b) => b.count - a.count); - - let tagPill; - if (tags.length) { - tagPill = tags.map((t) => ( - <TagPill key={t.id} name={t.name} count={t.count} /> - )); - } else { - tagPill = "No Tags"; - } - - return ( - <div className="container mt-2 space-y-3"> - <span className="text-2xl">All Tags</span> - <hr /> - <div className="flex flex-wrap gap-3">{tagPill}</div> - </div> - ); -} |
