fully added subcollection tree functionality

This commit is contained in:
daniel31x13 2024-03-04 08:56:16 -05:00
parent 51cf8172ff
commit 837241186f
4 changed files with 190 additions and 48 deletions

View File

@ -14,21 +14,27 @@ import { Collection } from "@prisma/client";
import Link from "next/link"; import Link from "next/link";
import { CollectionIncludingMembersAndLinkCount } from "@/types/global"; import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import useAccountStore from "@/store/account";
import toast from "react-hot-toast";
interface ExtendedTreeItem extends TreeItem { interface ExtendedTreeItem extends TreeItem {
data: Collection; data: Collection;
} }
const CollectionListing = () => { const CollectionListing = () => {
const { collections } = useCollectionStore(); const { collections, updateCollection } = useCollectionStore();
const { account, updateAccount } = useAccountStore();
const router = useRouter(); const router = useRouter();
const currentPath = router.asPath; const currentPath = router.asPath;
const initialTree = useMemo(() => { const initialTree = useMemo(() => {
if (collections.length > 0) { if (collections.length > 0) {
return buildTreeFromCollections(collections, router); return buildTreeFromCollections(
collections,
router,
account.collectionOrder
);
} }
return undefined; return undefined;
}, [collections, router]); }, [collections, router]);
@ -39,26 +45,155 @@ const CollectionListing = () => {
setTree(initialTree); setTree(initialTree);
}, [initialTree]); }, [initialTree]);
const onExpand = (itemId: ItemId) => { useEffect(() => {
if (account.username) {
if (!account.collectionOrder || account.collectionOrder.length === 0)
updateAccount({
...account,
collectionOrder: collections
.filter((e) => e.parentId === null) // Filter out collections with non-null parentId
.map((e) => e.id as number), // Use "as number" to assert that e.id is a number
});
else {
// const collectionsIds = collections.map((c) => c.id);
// const orderIds = [...account.collectionOrder];
// const missingInOrder = collectionsIds.filter(
// (id) => !orderIds.includes(id)
// );
// if (missingInOrder.length > 0) {
// updateAccount({
// ...account,
// collectionOrder: [...account.collectionOrder, ...missingInOrder],
// });
// }
}
}
}, [account, collections]);
const onExpand = (movedCollectionId: ItemId) => {
setTree((currentTree) => setTree((currentTree) =>
mutateTree(currentTree!, itemId, { isExpanded: true }) mutateTree(currentTree!, movedCollectionId, { isExpanded: true })
); );
}; };
const onCollapse = (itemId: ItemId) => { const onCollapse = (movedCollectionId: ItemId) => {
setTree((currentTree) => setTree((currentTree) =>
mutateTree(currentTree as TreeData, itemId, { isExpanded: false }) mutateTree(currentTree as TreeData, movedCollectionId, {
isExpanded: false,
})
); );
}; };
const onDragEnd = ( const onDragEnd = async (
source: TreeSourcePosition, source: TreeSourcePosition,
destination: TreeDestinationPosition | undefined destination: TreeDestinationPosition | undefined
) => { ) => {
if (!destination || !tree) { if (!destination || !tree) {
return; return;
} }
if (
source.index === destination.index &&
source.parentId === destination.parentId
) {
return;
}
const movedCollectionId = Number(
tree.items[source.parentId].children[source.index]
);
const movedCollection = collections.find((c) => c.id === movedCollectionId);
const destinationCollection = collections.find(
(c) => c.id === Number(destination.parentId)
);
console.log(
"Moved:",
movedCollection,
"Destination:",
destinationCollection
);
if (
(movedCollection?.ownerId !== account.id &&
destination.parentId !== source.parentId) ||
(destinationCollection?.ownerId !== account.id &&
destination.parentId !== "root")
) {
return toast.error(
"You can't make change to a collection you don't own."
);
}
console.log("source:", source, "destination:", destination);
setTree((currentTree) => moveItemOnTree(currentTree!, source, destination)); setTree((currentTree) => moveItemOnTree(currentTree!, source, destination));
const updatedCollectionOrder = [...account.collectionOrder];
if (source.parentId !== destination.parentId) {
await updateCollection({
...movedCollection,
parentId:
destination.parentId && destination.parentId !== "root"
? Number(destination.parentId)
: destination.parentId === "root"
? "root"
: null,
} as any);
}
if (
destination.index !== undefined &&
destination.parentId === source.parentId &&
source.parentId === "root"
) {
updatedCollectionOrder.splice(source.index, 1);
console.log("Order1", updatedCollectionOrder);
updatedCollectionOrder.splice(destination.index, 0, movedCollectionId);
console.log("Order2", updatedCollectionOrder);
console.log("Moved id:", movedCollectionId);
console.log("Order:", updatedCollectionOrder);
await updateAccount({
...account,
collectionOrder: updatedCollectionOrder,
});
} else if (
destination.index !== undefined &&
destination.parentId === "root"
) {
console.log("Order1", updatedCollectionOrder);
updatedCollectionOrder.splice(destination.index, 0, movedCollectionId);
console.log("Order2", updatedCollectionOrder);
console.log("Moved id:", movedCollectionId);
console.log("Order:", updatedCollectionOrder);
await updateAccount({
...account,
collectionOrder: updatedCollectionOrder,
});
} else if (
source.parentId === "root" &&
destination.parentId &&
destination.parentId !== "root"
) {
updatedCollectionOrder.splice(source.index, 1);
console.log("Order", updatedCollectionOrder);
await updateAccount({
...account,
collectionOrder: updatedCollectionOrder,
});
}
}; };
if (!tree) { if (!tree) {
@ -148,8 +283,15 @@ const Icon = (
const buildTreeFromCollections = ( const buildTreeFromCollections = (
collections: CollectionIncludingMembersAndLinkCount[], collections: CollectionIncludingMembersAndLinkCount[],
router: ReturnType<typeof useRouter> router: ReturnType<typeof useRouter>,
order?: number[]
): TreeData => { ): TreeData => {
if (order) {
collections.sort((a: any, b: any) => {
return order.indexOf(a.id) - order.indexOf(b.id);
});
}
const items: { [key: string]: ExtendedTreeItem } = collections.reduce( const items: { [key: string]: ExtendedTreeItem } = collections.reduce(
(acc: any, collection) => { (acc: any, collection) => {
acc[collection.id as number] = { acc[collection.id as number] = {
@ -177,8 +319,6 @@ const buildTreeFromCollections = (
{} {}
); );
console.log("items:", items);
const activeCollectionId = Number(router.asPath.split("/collections/")[1]); const activeCollectionId = Number(router.asPath.split("/collections/")[1]);
if (activeCollectionId) { if (activeCollectionId) {

View File

@ -65,7 +65,7 @@ export default function Navbar() {
<ToggleDarkMode className="hidden sm:inline-grid" /> <ToggleDarkMode className="hidden sm:inline-grid" />
<div className="dropdown dropdown-end sm:inline-block hidden"> <div className="dropdown dropdown-end sm:inline-block hidden">
<div className="tooltip tooltip-bottom" data-tip="Create New..."> <div className="tooltip tooltip-bottom z-10" data-tip="Create New...">
<div <div
tabIndex={0} tabIndex={0}
role="button" role="button"

View File

@ -18,24 +18,30 @@ export default async function updateCollection(
if (!(collectionIsAccessible?.ownerId === userId)) if (!(collectionIsAccessible?.ownerId === userId))
return { response: "Collection is not accessible.", status: 401 }; return { response: "Collection is not accessible.", status: 401 };
if (data.parentId) { console.log(data);
const findParentCollection = await prisma.collection.findUnique({
where: {
id: data.parentId,
},
select: {
ownerId: true,
},
});
if ( if (data.parentId) {
findParentCollection?.ownerId !== userId || if (data.parentId !== ("root" as any)) {
typeof data.parentId !== "number" const findParentCollection = await prisma.collection.findUnique({
) where: {
return { id: data.parentId,
response: "You are not authorized to create a sub-collection here.", },
status: 403, select: {
}; ownerId: true,
parentId: true,
},
});
if (
findParentCollection?.ownerId !== userId ||
typeof data.parentId !== "number" ||
findParentCollection?.parentId === data.parentId
)
return {
response: "You are not authorized to create a sub-collection here.",
status: 403,
};
}
} }
const updatedCollection = await prisma.$transaction(async () => { const updatedCollection = await prisma.$transaction(async () => {
@ -47,17 +53,6 @@ export default async function updateCollection(
}, },
}); });
await prisma.user.update({
where: {
id: userId,
},
data: {
collectionOrder: {
push: collectionId,
},
},
});
return await prisma.collection.update({ return await prisma.collection.update({
where: { where: {
id: collectionId, id: collectionId,
@ -67,13 +62,18 @@ export default async function updateCollection(
description: data.description, description: data.description,
color: data.color, color: data.color,
isPublic: data.isPublic, isPublic: data.isPublic,
parent: data.parentId parent:
? { data.parentId && data.parentId !== ("root" as any)
connect: { ? {
id: data.parentId, connect: {
}, id: data.parentId,
} },
: undefined, }
: data.parentId === ("root" as any)
? {
disconnect: true,
}
: undefined,
members: { members: {
create: data.members.map((e) => ({ create: data.members.map((e) => ({
user: { connect: { id: e.user.id || e.userId } }, user: { connect: { id: e.user.id || e.userId } },

View File

@ -183,7 +183,9 @@ export default async function updateUserById(
email: data.email?.toLowerCase().trim(), email: data.email?.toLowerCase().trim(),
isPrivate: data.isPrivate, isPrivate: data.isPrivate,
image: data.image ? `uploads/avatar/${userId}.jpg` : "", image: data.image ? `uploads/avatar/${userId}.jpg` : "",
collectionOrder: data.collectionOrder, collectionOrder: data.collectionOrder.filter(
(value, index, self) => self.indexOf(value) === index
),
archiveAsScreenshot: data.archiveAsScreenshot, archiveAsScreenshot: data.archiveAsScreenshot,
archiveAsPDF: data.archiveAsPDF, archiveAsPDF: data.archiveAsPDF,
archiveAsWaybackMachine: data.archiveAsWaybackMachine, archiveAsWaybackMachine: data.archiveAsWaybackMachine,