aboutsummaryrefslogtreecommitdiffstats
path: root/packages/web/app/dashboard
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-03-13 21:43:44 +0000
committerMohamed Bassem <me@mbassem.com>2024-03-14 16:40:45 +0000
commit04572a8e5081b1e4871e273cde9dbaaa44c52fe0 (patch)
tree8e993acb732a50d1306d4d6953df96c165c57f57 /packages/web/app/dashboard
parent2df08ed08c065e8b91bc8df0266bd4bcbb062be4 (diff)
downloadkarakeep-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.tsx203
-rw-r--r--packages/web/app/dashboard/archive/page.tsx9
-rw-r--r--packages/web/app/dashboard/bookmarks/layout.tsx23
-rw-r--r--packages/web/app/dashboard/bookmarks/loading.tsx11
-rw-r--r--packages/web/app/dashboard/bookmarks/page.tsx5
-rw-r--r--packages/web/app/dashboard/error.tsx9
-rw-r--r--packages/web/app/dashboard/favourites/page.tsx14
-rw-r--r--packages/web/app/dashboard/layout.tsx24
-rw-r--r--packages/web/app/dashboard/lists/[listId]/page.tsx44
-rw-r--r--packages/web/app/dashboard/lists/page.tsx14
-rw-r--r--packages/web/app/dashboard/not-found.tsx7
-rw-r--r--packages/web/app/dashboard/preview/[bookmarkId]/page.tsx14
-rw-r--r--packages/web/app/dashboard/search/page.tsx41
-rw-r--r--packages/web/app/dashboard/settings/page.tsx9
-rw-r--r--packages/web/app/dashboard/tags/[tagName]/page.tsx55
-rw-r--r--packages/web/app/dashboard/tags/page.tsx56
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>
- );
-}