From 70ef8cd2000b3218e66e0e7e6425487d329221cd Mon Sep 17 00:00:00 2001 From: Marc Seitz Date: Sat, 27 Jul 2024 12:50:06 +0300 Subject: [PATCH 1/5] feat: remove folders and contents from dataroom --- components/documents/folder-card.tsx | 15 ++++-- .../[id]/folders/manage/[folderId]/index.ts | 48 ++++++++++++------- 2 files changed, 41 insertions(+), 22 deletions(-) diff --git a/components/documents/folder-card.tsx b/components/documents/folder-card.tsx index 2cb28565b..3bdf39ade 100644 --- a/components/documents/folder-card.tsx +++ b/components/documents/folder-card.tsx @@ -103,7 +103,7 @@ export default function FolderCard({ }, ), { - loading: "Deleting folder...", + loading: isDataroom ? "Removing folder..." : "Deleting folder...", success: () => { mutate( `/api/teams/${teamInfo?.currentTeam?.id}/${endpointTargetType}?root=true`, @@ -114,9 +114,13 @@ export default function FolderCard({ mutate( `/api/teams/${teamInfo?.currentTeam?.id}/${endpointTargetType}${parentFolderPath}`, ); - return "Folder deleted successfully."; + return isDataroom + ? "Folder removed successfully." + : "Folder deleted successfully."; }, - error: "Failed to delete folder. Move documents first.", + error: isDataroom + ? "Failed to remove folder." + : "Failed to delete folder. Move documents first.", }, ); }; @@ -242,10 +246,11 @@ export default function FolderCard({ className="text-destructive duration-200 focus:bg-destructive focus:text-destructive-foreground" > {isFirstClick ? ( - "Really delete?" + `Really ${isDataroom ? "remove" : "delete"}?` ) : ( <> - Delete Folder + {" "} + {isDataroom ? "Remove Folder" : "Delete Folder"} )} diff --git a/pages/api/teams/[teamId]/datarooms/[id]/folders/manage/[folderId]/index.ts b/pages/api/teams/[teamId]/datarooms/[id]/folders/manage/[folderId]/index.ts index d254a177d..886f5970e 100644 --- a/pages/api/teams/[teamId]/datarooms/[id]/folders/manage/[folderId]/index.ts +++ b/pages/api/teams/[teamId]/datarooms/[id]/folders/manage/[folderId]/index.ts @@ -64,28 +64,16 @@ export default async function handle( id: folderId, dataroomId: dataroomId, }, - select: { - _count: { - select: { - documents: true, - childFolders: true, - }, - }, - }, }); - if (folder?._count.documents! > 0 || folder?._count.childFolders! > 0) { - return res.status(401).json({ - message: "Folder contains documents or folders. Move them first", + if (!folder) { + return res.status(404).json({ + message: "Folder not found", }); } - await prisma.dataroomFolder.delete({ - where: { - id: folderId, - dataroomId: dataroomId, - }, - }); + // Delete the folder and its contents recursively + await deleteFolderAndContents(folderId); return res.status(204).end(); // 204 No Content response for successful deletes } catch (error) { @@ -97,3 +85,29 @@ export default async function handle( return res.status(405).end(`Method ${req.method} Not Allowed`); } } + +async function deleteFolderAndContents(folderId: string) { + const childFoldersToDelete = await prisma.dataroomFolder.findMany({ + where: { + parentId: folderId, + }, + }); + + console.log("Deleting folder and contents", childFoldersToDelete); + + for (const folder of childFoldersToDelete) { + await deleteFolderAndContents(folder.id); + } + + await prisma.dataroomDocument.deleteMany({ + where: { + folderId: folderId, + }, + }); + + await prisma.dataroomFolder.delete({ + where: { + id: folderId, + }, + }); +} From 0ff039aa0e41ff694aa8023e0f53b17177496edf Mon Sep 17 00:00:00 2001 From: Marc Seitz Date: Sat, 27 Jul 2024 12:50:15 +0300 Subject: [PATCH 2/5] feat: remove documents from dataroom --- .../datarooms/dataroom-document-card.tsx | 119 +++++++++++++++--- components/documents/documents-list.tsx | 1 + .../[id]/documents/[documentId]/index.ts | 55 ++++++++ 3 files changed, 161 insertions(+), 14 deletions(-) diff --git a/components/datarooms/dataroom-document-card.tsx b/components/datarooms/dataroom-document-card.tsx index b9932a672..96b703bd7 100644 --- a/components/datarooms/dataroom-document-card.tsx +++ b/components/datarooms/dataroom-document-card.tsx @@ -1,5 +1,6 @@ import Image from "next/image"; import Link from "next/link"; +import { useRouter } from "next/router"; import { useEffect, useRef, useState } from "react"; @@ -34,21 +35,26 @@ import { MoveToDataroomFolderModal } from "./move-dataroom-folder-modal"; type DocumentsCardProps = { document: DataroomFolderDocument; teamInfo: TeamContextType | null; + dataroomId: string; }; export default function DataroomDocumentCard({ document: dataroomDocument, teamInfo, + dataroomId, }: DocumentsCardProps) { const { theme, systemTheme } = useTheme(); const isLight = theme === "light" || (theme === "system" && systemTheme === "light"); + const router = useRouter(); - const { isCopied, copyToClipboard } = useCopyToClipboard({}); const [isFirstClick, setIsFirstClick] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [moveFolderOpen, setMoveFolderOpen] = useState(false); const dropdownRef = useRef(null); + /** current folder name */ + const currentFolderPath = router.query.name as string[] | undefined; + // https://github.com/radix-ui/primitives/issues/1241#issuecomment-1888232392 useEffect(() => { if (!moveFolderOpen) { @@ -58,6 +64,89 @@ export default function DataroomDocumentCard({ } }, [moveFolderOpen]); + useEffect(() => { + function handleClickOutside(event: { target: any }) { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setMenuOpen(false); + setIsFirstClick(false); + } + } + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + const handleButtonClick = (event: any, documentId: string) => { + event.stopPropagation(); + event.preventDefault(); + + console.log("isFirstClick", isFirstClick); + if (isFirstClick) { + handleRemoveDocument(documentId); + setIsFirstClick(false); + setMenuOpen(false); // Close the dropdown after deleting + } else { + setIsFirstClick(true); + } + }; + + const handleRemoveDocument = async (documentId: string) => { + // Prevent the first click from deleting the document + if (!isFirstClick) { + setIsFirstClick(true); + return; + } + + const endpoint = currentFolderPath + ? `/folders/documents/${currentFolderPath.join("/")}` + : "/documents"; + + toast.promise( + fetch( + `/api/teams/${teamInfo?.currentTeam?.id}/datarooms/${dataroomId}/documents/${documentId}`, + { + method: "DELETE", + }, + ).then(() => { + mutate( + `/api/teams/${teamInfo?.currentTeam?.id}/datarooms/${dataroomId}${endpoint}`, + null, + { + populateCache: (_, docs) => { + return docs.filter( + (doc: DocumentWithLinksAndLinkCountAndViewCount) => + doc.id !== documentId, + ); + }, + revalidate: false, + }, + ); + }), + { + loading: "Removing document...", + success: "Document removed successfully.", + error: "Failed to remove document. Try again.", + }, + ); + }; + + const handleMenuStateChange = (open: boolean) => { + if (isFirstClick) { + setMenuOpen(true); // Keep the dropdown open on the first click + return; + } + + // If the menu is closed, reset the isFirstClick state + if (!open) { + setIsFirstClick(false); + setMenuOpen(false); // Ensure the dropdown is closed + } else { + setMenuOpen(true); // Open the dropdown + } + }; + return ( <>
  • @@ -114,7 +203,7 @@ export default function DataroomDocumentCard({

    - +
    diff --git a/pages/api/teams/[teamId]/billing/manage.ts b/pages/api/teams/[teamId]/billing/manage.ts index f24fed6a7..2819327ea 100644 --- a/pages/api/teams/[teamId]/billing/manage.ts +++ b/pages/api/teams/[teamId]/billing/manage.ts @@ -5,6 +5,7 @@ import { getServerSession } from "next-auth/next"; import { errorhandler } from "@/lib/errorHandler"; import prisma from "@/lib/prisma"; import { stripe } from "@/lib/stripe"; +import { CustomUser } from "@/lib/types"; import { authOptions } from "../../../auth/[...nextauth]"; @@ -25,6 +26,11 @@ export default async function handle( const team = await prisma.team.findUnique({ where: { id: teamId, + users: { + some: { + userId: (session.user as CustomUser).id, + }, + }, }, select: { stripeId: true,