From e753f1ddedb33c161bf22b7e583b874241ee1b0b Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Sun, 11 Feb 2024 00:19:59 -0600 Subject: [PATCH] bulk actions on tags page --- hooks/useCollectivePermissions.ts | 34 +++++++++ hooks/usePermissions.tsx | 11 +-- pages/collections/[id].tsx | 17 +++-- pages/tags/[id].tsx | 116 ++++++++++++++++++++++++++++-- 4 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 hooks/useCollectivePermissions.ts diff --git a/hooks/useCollectivePermissions.ts b/hooks/useCollectivePermissions.ts new file mode 100644 index 00000000..c989e603 --- /dev/null +++ b/hooks/useCollectivePermissions.ts @@ -0,0 +1,34 @@ +import useAccountStore from "@/store/account"; +import useCollectionStore from "@/store/collections"; +import { Member } from "@/types/global"; +import { useEffect, useState } from "react"; + +export default function useCollectivePermissions(collectionIds: number[]) { + const { collections } = useCollectionStore(); + + const { account } = useAccountStore(); + + const [permissions, setPermissions] = useState(); + useEffect(() => { + for (const collectionId of collectionIds) { + const collection = collections.find((e) => e.id === collectionId); + + if (collection) { + let getPermission: Member | undefined = collection.members.find( + (e) => e.userId === account.id + ); + + if ( + getPermission?.canCreate === false && + getPermission?.canUpdate === false && + getPermission?.canDelete === false + ) + getPermission = undefined; + + setPermissions(account.id === collection.ownerId || getPermission); + } + } + }, [account, collections, collectionIds]); + + return permissions; +} diff --git a/hooks/usePermissions.tsx b/hooks/usePermissions.tsx index 2faa1058..354e4cc7 100644 --- a/hooks/usePermissions.tsx +++ b/hooks/usePermissions.tsx @@ -1,13 +1,15 @@ import useAccountStore from "@/store/account"; import useCollectionStore from "@/store/collections"; import { Member } from "@/types/global"; -import { useMemo } from "react"; +import { useEffect, useState } from "react"; export default function usePermissions(collectionId: number) { const { collections } = useCollectionStore(); + const { account } = useAccountStore(); - const permissions = useMemo(() => { + const [permissions, setPermissions] = useState(); + useEffect(() => { const collection = collections.find((e) => e.id === collectionId); if (collection) { @@ -19,11 +21,10 @@ export default function usePermissions(collectionId: number) { getPermission?.canCreate === false && getPermission?.canUpdate === false && getPermission?.canDelete === false - ) { + ) getPermission = undefined; - } - return account.id === collection.ownerId || getPermission; + setPermissions(account.id === collection.ownerId || getPermission); } }, [account, collections, collectionId]); diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index 44cec4ab..0a5196a7 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -312,7 +312,10 @@ export default function Index() {
setEditMode(!editMode)} + onClick={() => { + setEditMode(!editMode) + setSelectedLinks([]) + }} className={`btn btn-square btn-sm btn-ghost ${editMode ? "bg-primary/20 hover:bg-primary/20" : "hover:bg-neutral/20" @@ -371,7 +374,6 @@ export default function Index() { )}
- )} @@ -414,11 +416,18 @@ export default function Index() { )} {bulkDeleteLinksModal && ( setBulkDeleteLinksModal(false)} + onClose={() => { + setBulkDeleteLinksModal(false) + setEditMode(false) + }} /> )} {bulkEditLinksModal && ( - setBulkEditLinksModal(false)} /> + { + setBulkEditLinksModal(false) + setEditMode(false) + }} /> )} )} diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx index 932d26be..e2c97721 100644 --- a/pages/tags/[id].tsx +++ b/pages/tags/[id].tsx @@ -12,11 +12,14 @@ import CardView from "@/components/LinkViews/Layouts/CardView"; // import GridView from "@/components/LinkViews/Layouts/GridView"; import ListView from "@/components/LinkViews/Layouts/ListView"; import { dropdownTriggerer } from "@/lib/client/utils"; +import BulkDeleteLinksModal from "@/components/ModalContent/BulkDeleteLinksModal"; +import BulkEditLinksModal from "@/components/ModalContent/BulkEditLinksModal"; +import useCollectivePermissions from "@/hooks/useCollectivePermissions"; export default function Index() { const router = useRouter(); - const { links } = useLinkStore(); + const { links, selectedLinks, deleteLinksById, setSelectedLinks } = useLinkStore(); const { tags, updateTag, removeTag } = useTagStore(); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); @@ -26,6 +29,11 @@ export default function Index() { const [activeTag, setActiveTag] = useState(); + const [bulkDeleteLinksModal, setBulkDeleteLinksModal] = useState(false); + const [bulkEditLinksModal, setBulkEditLinksModal] = useState(false); + const [editMode, setEditMode] = useState(false); + const collectivePermissions = useCollectivePermissions(selectedLinks.map((link) => link.collectionId as number)); + useLinks({ tagId: Number(router.query.id), sort: sortBy }); useEffect(() => { @@ -91,6 +99,33 @@ export default function Index() { setRenameTag(false); }; + const handleSelectAll = () => { + if (selectedLinks.length === links.length) { + setSelectedLinks([]); + } else { + setSelectedLinks(links.map((link) => link)); + } + }; + + const bulkDeleteLinks = async () => { + const load = toast.loading( + `Deleting ${selectedLinks.length} Link${selectedLinks.length > 1 ? "s" : "" + }...` + ); + + const response = await deleteLinksById( + selectedLinks.map((link) => link.id as number) + ); + + toast.dismiss(load); + + response.ok && + toast.success( + `Deleted ${selectedLinks.length} Link${selectedLinks.length > 1 ? "s" : "" + }!` + ); + }; + const [viewMode, setViewMode] = useState( localStorage.getItem("viewMode") || ViewMode.Card ); @@ -145,11 +180,10 @@ export default function Index() {

8 - ? "dropdown-end" - : "" - }`} + className={`dropdown dropdown-bottom font-normal ${activeTag?.name.length && activeTag?.name.length > 8 + ? "dropdown-end" + : "" + }`} >
+
{ + setEditMode(!editMode) + setSelectedLinks([]) + }} + className={`btn btn-square btn-sm btn-ghost ${editMode + ? "bg-primary/20 hover:bg-primary/20" + : "hover:bg-neutral/20" + }`} + > + +
+ {editMode && ( +
+ {links.length > 0 && ( +
+ handleSelectAll()} + checked={ + selectedLinks.length === links.length && links.length > 0 + } + /> + {selectedLinks.length > 0 && ( + + {selectedLinks.length}{" "} + {selectedLinks.length === 1 ? "link" : "links"} selected + + )} +
+ )} +
+ {selectedLinks.length > 0 && + (collectivePermissions === true || collectivePermissions?.canUpdate) && ( + + )} + {selectedLinks.length > 0 && + (collectivePermissions === true || collectivePermissions?.canDelete) && ( + + )} +
+
+ )} e.tags.some((e) => e.id === Number(router.query.id)) )} />
+ {bulkDeleteLinksModal && ( + setBulkDeleteLinksModal(false)} + /> + )} + {bulkEditLinksModal && ( + setBulkEditLinksModal(false)} /> + )} ); }