From ddc7e5dbfc31b3e5b189c61883e1cc737fefd2ee Mon Sep 17 00:00:00 2001
From: MohamedBassem <me@mbassem.com>
Date: Sun, 1 Sep 2024 17:44:09 +0000
Subject: feature(web): Allow adding to lists in bulk actions. #368

---
 .../dashboard/bookmarks/BulkManageListsModal.tsx   | 138 +++++++++++++++++++++
 1 file changed, 138 insertions(+)
 create mode 100644 apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx

(limited to 'apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx')

diff --git a/apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx b/apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx
new file mode 100644
index 00000000..9c1f05d2
--- /dev/null
+++ b/apps/web/components/dashboard/bookmarks/BulkManageListsModal.tsx
@@ -0,0 +1,138 @@
+import { ActionButton } from "@/components/ui/action-button";
+import { Button } from "@/components/ui/button";
+import {
+  Dialog,
+  DialogClose,
+  DialogContent,
+  DialogFooter,
+  DialogHeader,
+  DialogTitle,
+} from "@/components/ui/dialog";
+import {
+  Form,
+  FormControl,
+  FormField,
+  FormItem,
+  FormMessage,
+} from "@/components/ui/form";
+import { toast } from "@/components/ui/use-toast";
+import { zodResolver } from "@hookform/resolvers/zod";
+import { useForm } from "react-hook-form";
+import { z } from "zod";
+
+import { useAddBookmarkToList } from "@hoarder/shared-react/hooks/lists";
+
+import { BookmarkListSelector } from "../lists/BookmarkListSelector";
+
+export default function BulkManageListsModal({
+  bookmarkIds,
+  open,
+  setOpen,
+}: {
+  bookmarkIds: string[];
+  open: boolean;
+  setOpen: (open: boolean) => void;
+}) {
+  const formSchema = z.object({
+    listId: z.string({
+      required_error: "Please select a list",
+    }),
+  });
+  const form = useForm<z.infer<typeof formSchema>>({
+    resolver: zodResolver(formSchema),
+    defaultValues: {
+      listId: undefined,
+    },
+  });
+
+  const { mutateAsync: addToList, isPending: isAddingToListPending } =
+    useAddBookmarkToList({
+      onSettled: () => {
+        form.resetField("listId");
+      },
+      onError: (e) => {
+        if (e.data?.code == "BAD_REQUEST") {
+          toast({
+            variant: "destructive",
+            description: e.message,
+          });
+        } else {
+          toast({
+            variant: "destructive",
+            title: "Something went wrong",
+          });
+        }
+      },
+    });
+
+  const onSubmit = async (value: z.infer<typeof formSchema>) => {
+    const results = await Promise.allSettled(
+      bookmarkIds.map((bookmarkId) =>
+        addToList({
+          bookmarkId,
+          listId: value.listId,
+        }),
+      ),
+    );
+
+    const successes = results.filter((r) => r.status == "fulfilled").length;
+    if (successes > 0) {
+      toast({
+        description: `${successes} bookmarks have been added to the list!`,
+      });
+    }
+
+    setOpen(false);
+  };
+
+  return (
+    <Dialog open={open} onOpenChange={setOpen}>
+      <DialogContent>
+        <Form {...form}>
+          <form
+            className="flex w-full flex-col gap-4"
+            onSubmit={form.handleSubmit(onSubmit)}
+          >
+            <DialogHeader>
+              <DialogTitle>
+                Add {bookmarkIds.length} bookmarks to List
+              </DialogTitle>
+            </DialogHeader>
+
+            <FormField
+              control={form.control}
+              name="listId"
+              render={({ field }) => {
+                return (
+                  <FormItem>
+                    <FormControl>
+                      <BookmarkListSelector
+                        value={field.value}
+                        onChange={field.onChange}
+                      />
+                    </FormControl>
+                    <FormMessage />
+                  </FormItem>
+                );
+              }}
+            />
+            <DialogFooter className="sm:justify-end">
+              <DialogClose asChild>
+                <Button type="button" variant="secondary">
+                  Close
+                </Button>
+              </DialogClose>
+              <ActionButton
+                type="submit"
+                loading={isAddingToListPending}
+                disabled={isAddingToListPending}
+              >
+                Add
+              </ActionButton>
+            </DialogFooter>
+          </form>
+        </Form>
+      </DialogContent>
+    </Dialog>
+  );
+}
-- 
cgit v1.2.3-70-g09d2

