aboutsummaryrefslogtreecommitdiffstats
path: root/packages/web/app/dashboard/lists
diff options
context:
space:
mode:
authorMohamedBassem <me@mbassem.com>2024-03-01 18:00:58 +0000
committerMohamedBassem <me@mbassem.com>2024-03-01 18:00:58 +0000
commit75d315dda4232ee3b89abf054f0b6ee10105ffe3 (patch)
treef0796a136578f3b5aa82b4b3313e54fa3061ff5f /packages/web/app/dashboard/lists
parent588471d65039e6920751ac2add8874ee932bc2f1 (diff)
downloadkarakeep-75d315dda4232ee3b89abf054f0b6ee10105ffe3.tar.zst
feature: Add support for creating and updating lists
Diffstat (limited to 'packages/web/app/dashboard/lists')
-rw-r--r--packages/web/app/dashboard/lists/[listId]/components/DeleteListButton.tsx76
-rw-r--r--packages/web/app/dashboard/lists/[listId]/components/ListView.tsx35
-rw-r--r--packages/web/app/dashboard/lists/[listId]/page.tsx32
3 files changed, 143 insertions, 0 deletions
diff --git a/packages/web/app/dashboard/lists/[listId]/components/DeleteListButton.tsx b/packages/web/app/dashboard/lists/[listId]/components/DeleteListButton.tsx
new file mode 100644
index 00000000..8961b2d0
--- /dev/null
+++ b/packages/web/app/dashboard/lists/[listId]/components/DeleteListButton.tsx
@@ -0,0 +1,76 @@
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog";
+import { Trash } from "lucide-react";
+import { useRouter } from "next/navigation";
+import { toast } from "@/components/ui/use-toast";
+import { api } from "@/lib/trpc";
+import { ActionButton } from "@/components/ui/action-button";
+import { useState } from "react";
+import { ZBookmarkList } from "@/lib/types/api/lists";
+
+export default function DeleteListButton({ list }: { list: ZBookmarkList }) {
+ const [isDialogOpen, setDialogOpen] = useState(false);
+
+ const router = useRouter();
+
+ const listsInvalidationFunction = api.useUtils().lists.list.invalidate;
+ const { mutate: deleteList, isPending } = api.lists.delete.useMutation({
+ onSuccess: () => {
+ listsInvalidationFunction();
+ toast({
+ description: `List "${list.icon} ${list.name}" is deleted!`,
+ });
+ router.push("/");
+ },
+ onError: () => {
+ toast({
+ variant: "destructive",
+ description: `Something went wrong`,
+ });
+ },
+ });
+ return (
+ <Dialog open={isDialogOpen} onOpenChange={setDialogOpen}>
+ <DialogTrigger asChild>
+ <Button className="mt-auto flex gap-2" variant="destructive">
+ <Trash className="size-5" />
+ <span className="hidden md:block">Delete List</span>
+ </Button>
+ </DialogTrigger>
+ <DialogContent>
+ <DialogHeader>
+ <DialogTitle>
+ Delete {list.icon} {list.name}?
+ </DialogTitle>
+ </DialogHeader>
+ <span>
+ Are you sure you want to delete {list.icon} {list.name}?
+ </span>
+ <DialogFooter className="sm:justify-end">
+ <DialogClose asChild>
+ <Button type="button" variant="secondary">
+ Close
+ </Button>
+ </DialogClose>
+ <ActionButton
+ type="button"
+ variant="destructive"
+ loading={isPending}
+ onClick={() => deleteList({ listId: list.id })}
+ >
+ Delete
+ </ActionButton>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ );
+}
diff --git a/packages/web/app/dashboard/lists/[listId]/components/ListView.tsx b/packages/web/app/dashboard/lists/[listId]/components/ListView.tsx
new file mode 100644
index 00000000..c3d49b6a
--- /dev/null
+++ b/packages/web/app/dashboard/lists/[listId]/components/ListView.tsx
@@ -0,0 +1,35 @@
+"use client";
+
+import BookmarksGrid from "@/app/dashboard/bookmarks/components/BookmarksGrid";
+import { ZBookmark } from "@/lib/types/api/bookmarks";
+import { ZBookmarkListWithBookmarks } from "@/lib/types/api/lists";
+import { api } from "@/lib/trpc";
+import DeleteListButton from "./DeleteListButton";
+
+export default function ListView({
+ bookmarks,
+ list: initialData,
+}: {
+ list: ZBookmarkListWithBookmarks;
+ bookmarks: ZBookmark[];
+}) {
+ const { data } = api.lists.get.useQuery(
+ { listId: initialData.id },
+ {
+ initialData,
+ },
+ );
+
+ return (
+ <div className="container flex flex-col gap-3">
+ <div className="flex justify-between">
+ <span className="pt-4 text-2xl">
+ {data.icon} {data.name}
+ </span>
+ <DeleteListButton list={data} />
+ </div>
+ <hr />
+ <BookmarksGrid query={{ ids: data.bookmarks }} bookmarks={bookmarks} />
+ </div>
+ );
+}
diff --git a/packages/web/app/dashboard/lists/[listId]/page.tsx b/packages/web/app/dashboard/lists/[listId]/page.tsx
new file mode 100644
index 00000000..b8ca79c3
--- /dev/null
+++ b/packages/web/app/dashboard/lists/[listId]/page.tsx
@@ -0,0 +1,32 @@
+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/ListView";
+
+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 <ListView list={list} bookmarks={bookmarks.bookmarks} />;
+}