diff --git a/components/CollectionSelection.tsx b/components/CollectionListing.tsx similarity index 100% rename from components/CollectionSelection.tsx rename to components/CollectionListing.tsx diff --git a/components/InputSelect/CollectionSelection.tsx b/components/InputSelect/CollectionSelection.tsx index ec586ea..99b999e 100644 --- a/components/InputSelect/CollectionSelection.tsx +++ b/components/InputSelect/CollectionSelection.tsx @@ -44,12 +44,56 @@ export default function CollectionSelection({ useEffect(() => { const formatedCollections = collections.map((e) => { - return { value: e.id, label: e.name, ownerId: e.ownerId }; + return { + value: e.id, + label: e.name, + ownerId: e.ownerId, + count: e._count, + parentId: e.parentId, + }; }); setOptions(formatedCollections); }, [collections]); + const getParentNames = (parentId: number): string[] => { + const parentNames = []; + const parent = collections.find((e) => e.id === parentId); + + if (parent) { + parentNames.push(parent.name); + if (parent.parentId) { + parentNames.push(...getParentNames(parent.parentId)); + } + } + + // Have the top level parent at beginning + return parentNames.reverse(); + }; + + const customOption = ({ data, innerProps }: any) => { + return ( +
+
+ {data.label} + {data.count?.links} +
+
+ {getParentNames(data?.parentId).length > 0 ? ( + <> + {getParentNames(data.parentId).join(" > ")} {">"} {data.label} + + ) : ( + data.label + )} +
+
+ ); + }; + if (creatable) { return ( ); @@ -73,6 +120,9 @@ export default function CollectionSelection({ options={options} styles={styles} defaultValue={showDefaultValue ? defaultValue : null} + components={{ + Option: customOption, + }} // menuPosition="fixed" /> ); diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx index 66a8d23..3f3e97f 100644 --- a/components/Sidebar.tsx +++ b/components/Sidebar.tsx @@ -5,7 +5,7 @@ import { useRouter } from "next/router"; import { useEffect, useState } from "react"; import { Disclosure, Transition } from "@headlessui/react"; import SidebarHighlightLink from "@/components/SidebarHighlightLink"; -import CollectionSelection from "@/components/CollectionSelection"; +import CollectionListing from "@/components/CollectionListing"; export default function Sidebar({ className }: { className?: string }) { const [tagDisclosure, setTagDisclosure] = useState(() => { @@ -99,7 +99,7 @@ export default function Sidebar({ className }: { className?: string }) { leaveTo="transform opacity-0 -translate-y-3" > - + diff --git a/lib/api/controllers/collections/getCollections.ts b/lib/api/controllers/collections/getCollections.ts index 3742163..fa13798 100644 --- a/lib/api/controllers/collections/getCollections.ts +++ b/lib/api/controllers/collections/getCollections.ts @@ -12,6 +12,12 @@ export default async function getCollection(userId: number) { _count: { select: { links: true }, }, + parent: { + select: { + id: true, + name: true, + }, + }, members: { include: { user: { diff --git a/lib/api/controllers/collections/postCollection.ts b/lib/api/controllers/collections/postCollection.ts index 245f642..94f4763 100644 --- a/lib/api/controllers/collections/postCollection.ts +++ b/lib/api/controllers/collections/postCollection.ts @@ -32,27 +32,6 @@ export default async function postCollection( }; } - const findCollection = await prisma.user.findUnique({ - where: { - id: userId, - }, - select: { - collections: { - where: { - name: collection.name, - }, - }, - }, - }); - - const checkIfCollectionExists = findCollection?.collections[0]; - - if (checkIfCollectionExists) - return { - response: "Oops! There's already a Collection with that name.", - status: 400, - }; - const newCollection = await prisma.collection.create({ data: { owner: { @@ -65,10 +44,10 @@ export default async function postCollection( color: collection.color, parent: collection.parentId ? { - connect: { - id: collection.parentId, - }, - } + connect: { + id: collection.parentId, + }, + } : undefined, }, include: { diff --git a/lib/api/controllers/links/postLink.ts b/lib/api/controllers/links/postLink.ts index 1c82fc2..eb82949 100644 --- a/lib/api/controllers/links/postLink.ts +++ b/lib/api/controllers/links/postLink.ts @@ -22,8 +22,69 @@ export default async function postLink( }; } - if (!link.collection.name) { + if (!link.collection.id && link.collection.name) { + link.collection.name = link.collection.name.trim(); + + // find the collection with the name and the user's id + const findCollection = await prisma.collection.findFirst({ + where: { + name: link.collection.name, + ownerId: userId, + parentId: link.collection.parentId, + }, + }); + + if (findCollection) { + const collectionIsAccessible = await getPermission({ + userId, + collectionId: findCollection.id, + }); + + const memberHasAccess = collectionIsAccessible?.members.some( + (e: UsersAndCollections) => e.userId === userId && e.canCreate + ); + + if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) + return { response: "Collection is not accessible.", status: 401 }; + + link.collection.id = findCollection.id; + } else { + const collection = await prisma.collection.create({ + data: { + name: link.collection.name, + ownerId: userId, + }, + }); + + link.collection.id = collection.id; + } + } else if (link.collection.id) { + const collectionIsAccessible = await getPermission({ + userId, + collectionId: link.collection.id, + }); + + const memberHasAccess = collectionIsAccessible?.members.some( + (e: UsersAndCollections) => e.userId === userId && e.canCreate + ); + + if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) + return { response: "Collection is not accessible.", status: 401 }; + } else if (!link.collection.id) { link.collection.name = "Unorganized"; + link.collection.parentId = null; + + // find the collection with the name "Unorganized" and the user's id + const unorganizedCollection = await prisma.collection.findFirst({ + where: { + name: "Unorganized", + ownerId: userId, + }, + }); + + link.collection.id = unorganizedCollection?.id; + } else { + return { response: "Uncaught error.", status: 500 }; } const numberOfLinksTheUserHas = await prisma.link.count({ @@ -42,22 +103,6 @@ export default async function postLink( link.collection.name = link.collection.name.trim(); - if (link.collection.id) { - const collectionIsAccessible = await getPermission({ - userId, - collectionId: link.collection.id, - }); - - const memberHasAccess = collectionIsAccessible?.members.some( - (e: UsersAndCollections) => e.userId === userId && e.canCreate - ); - - if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) - return { response: "Collection is not accessible.", status: 401 }; - } else { - link.collection.ownerId = userId; - } - const description = link.description && link.description !== "" ? link.description @@ -86,17 +131,8 @@ export default async function postLink( description, type: linkType, collection: { - connectOrCreate: { - where: { - name_ownerId: { - ownerId: link.collection.ownerId, - name: link.collection.name, - }, - }, - create: { - name: link.collection.name.trim(), - ownerId: userId, - }, + connect: { + id: link.collection.id, }, }, tags: { diff --git a/prisma/migrations/20240218080348_allow_duplicate_collection_names/migration.sql b/prisma/migrations/20240218080348_allow_duplicate_collection_names/migration.sql new file mode 100644 index 0000000..d73171b --- /dev/null +++ b/prisma/migrations/20240218080348_allow_duplicate_collection_names/migration.sql @@ -0,0 +1,2 @@ +-- DropIndex +DROP INDEX "Collection_name_ownerId_key"; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 036f658..4d9ccec 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -91,8 +91,6 @@ model Collection { links Link[] createdAt DateTime @default(now()) updatedAt DateTime @default(now()) @updatedAt - - @@unique([name, ownerId]) } model UsersAndCollections {