From 4bfb08a52e2fa358dae09af838209d07a93df235 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 25 Apr 2023 01:00:40 +0330 Subject: [PATCH] feat: add collection functionality --- components/Modal/AddCollection.tsx | 109 +++++++++--------- components/Modal/AddLink.tsx | 2 +- components/Sidebar/index.tsx | 48 +------- .../controllers/collections/postCollection.ts | 12 +- pages/api/routes/collections/index.ts | 5 +- .../20230328031049_init/migration.sql | 2 - .../migrations/20230328072406_/migration.sql | 11 -- .../migration.sql | 8 +- prisma/schema.prisma | 1 + store/collections.ts | 9 +- store/links.ts | 8 +- types/global.ts | 5 + 12 files changed, 86 insertions(+), 134 deletions(-) delete mode 100644 prisma/migrations/20230328031049_init/migration.sql delete mode 100644 prisma/migrations/20230328072406_/migration.sql rename prisma/migrations/{20230327225044_init => 20230424185742_}/migration.sql (90%) diff --git a/components/Modal/AddCollection.tsx b/components/Modal/AddCollection.tsx index 61eb8ae..04d207a 100644 --- a/components/Modal/AddCollection.tsx +++ b/components/Modal/AddCollection.tsx @@ -3,64 +3,69 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . -// import React, { useState } from "react"; -// import CollectionSelection from "@/components/InputSelect/CollectionSelection"; -// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -// import { faPlus } from "@fortawesome/free-solid-svg-icons"; -// import useCollectionStore from "@/store/collections"; +import React, { useState } from "react"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faPlus } from "@fortawesome/free-solid-svg-icons"; +import useCollectionStore from "@/store/collections"; +import { NewCollection } from "@/types/global"; -// type Props = { -// toggleCollectionModal: Function; -// }; +type Props = { + toggleCollectionModal: Function; +}; -// export default function AddCollection({ toggleCollectionModal }: Props) { -// const [newCollection, setNewCollection] = useState({ -// name: "", -// ownerId: undefined, -// }); +export default function AddCollection({ toggleCollectionModal }: Props) { + const [newCollection, setNewCollection] = useState({ + name: "", + description: "", + }); -// const { addCollection } = useCollectionStore(); + const { addCollection } = useCollectionStore(); -// const setCollectionOwner = (e: any) => { -// if (e?.__isNew__) e.value = null; + const submitCollection = async () => { + console.log(newCollection); -// setNewCollection({ -// ...newCollection, -// ownerId: e?.value, -// }); -// }; + const response = await addCollection(newCollection as NewCollection); -// return ( -//
-//

New Collection

+ if (response) toggleCollectionModal(); + }; -//
-//

Name

-// -// setNewCollection({ ...newCollection, name: e.target.value }) -// } -// type="text" -// placeholder="e.g. Example Collection" -// className="w-full rounded-md p-3 border-sky-100 border-solid border text-sm outline-none focus:border-sky-500 duration-100" -// /> -//
+ return ( +
+

New Collection

-//
-//

Owner

-// -//
+
+

Name

+ + setNewCollection({ ...newCollection, name: e.target.value }) + } + type="text" + placeholder="e.g. Example Collection" + className="w-96 rounded-md p-3 border-sky-100 border-solid border text-sm outline-none focus:border-sky-500 duration-100" + /> +
-//
-// -//
-//
-// ); -// } +
+

Description

+ + setNewCollection({ ...newCollection, description: e.target.value }) + } + type="text" + placeholder="Collection description (Optional)" + className="w-96 rounded-md p-3 border-sky-100 border-solid border text-sm outline-none focus:border-sky-500 duration-100" + /> +
+ +
+ + Add Collection +
+
+ ); +} diff --git a/components/Modal/AddLink.tsx b/components/Modal/AddLink.tsx index 399192e..b5fce22 100644 --- a/components/Modal/AddLink.tsx +++ b/components/Modal/AddLink.tsx @@ -70,7 +70,7 @@ export default function AddLink({ toggleLinkModal }: Props) { const submitLink = async () => { console.log(newLink); - const response = await addLink(newLink as ExtendedLink); + const response = await addLink(newLink as NewLink); if (response) toggleLinkModal(); }; diff --git a/components/Sidebar/index.tsx b/components/Sidebar/index.tsx index f6149fb..63e97ff 100644 --- a/components/Sidebar/index.tsx +++ b/components/Sidebar/index.tsx @@ -3,12 +3,9 @@ // This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. // You should have received a copy of the GNU General Public License along with this program. If not, see . -import ClickAwayHandler from "@/components/ClickAwayHandler"; -import { useState } from "react"; import useCollectionStore from "@/store/collections"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { - faPlus, faFolder, faBox, faHashtag, @@ -19,27 +16,10 @@ import useTagStore from "@/store/tags"; import Link from "next/link"; export default function () { - const [collectionInput, setCollectionInput] = useState(false); - - const { collections, addCollection } = useCollectionStore(); + const { collections } = useCollectionStore(); const { tags } = useTagStore(); - const toggleCollectionInput = () => { - setCollectionInput(!collectionInput); - }; - - const submitCollection = async ( - event: React.KeyboardEvent - ) => { - const collectionName: string = (event.target as HTMLInputElement).value; - - if (event.key === "Enter" && collectionName) { - addCollection(collectionName); - (event.target as HTMLInputElement).value = ""; - } - }; - return (

@@ -60,30 +40,8 @@ export default function () {

-
+

Collections

- {collectionInput ? ( - - - - ) : ( -
- -
- )}
{collections.map((e, i) => { @@ -97,7 +55,7 @@ export default function () { ); })}
-
+

Tags

diff --git a/lib/api/controllers/collections/postCollection.ts b/lib/api/controllers/collections/postCollection.ts index ed270a9..2a26b72 100644 --- a/lib/api/controllers/collections/postCollection.ts +++ b/lib/api/controllers/collections/postCollection.ts @@ -4,12 +4,13 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . import { prisma } from "@/lib/api/db"; +import { Collection } from "@prisma/client"; import { existsSync, mkdirSync } from "fs"; -export default async function (collectionName: string, userId: number) { - if (!collectionName) +export default async function (collection: Collection, userId: number) { + if (!collection) return { - response: "Please enter a valid name for the collection.", + response: "Please enter a valid collection.", status: 400, }; @@ -20,7 +21,7 @@ export default async function (collectionName: string, userId: number) { select: { collections: { where: { - name: collectionName, + name: collection.name, }, }, }, @@ -38,7 +39,8 @@ export default async function (collectionName: string, userId: number) { id: userId, }, }, - name: collectionName, + name: collection.name, + description: collection.description, }, }); diff --git a/pages/api/routes/collections/index.ts b/pages/api/routes/collections/index.ts index 86f43fd..e4ba78d 100644 --- a/pages/api/routes/collections/index.ts +++ b/pages/api/routes/collections/index.ts @@ -22,10 +22,7 @@ export default async function (req: NextApiRequest, res: NextApiResponse) { .status(collections.status) .json({ response: collections.response }); } else if (req.method === "POST") { - const newCollection = await postCollection( - req.body.collectionName.trim(), - session.user.id - ); + const newCollection = await postCollection(req.body, session.user.id); return res .status(newCollection.status) .json({ response: newCollection.response }); diff --git a/prisma/migrations/20230328031049_init/migration.sql b/prisma/migrations/20230328031049_init/migration.sql deleted file mode 100644 index 562ef5b..0000000 --- a/prisma/migrations/20230328031049_init/migration.sql +++ /dev/null @@ -1,2 +0,0 @@ --- DropIndex -DROP INDEX "Tag_name_collectionId_key"; diff --git a/prisma/migrations/20230328072406_/migration.sql b/prisma/migrations/20230328072406_/migration.sql deleted file mode 100644 index a70afb8..0000000 --- a/prisma/migrations/20230328072406_/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `collectionId` on the `Tag` table. All the data in the column will be lost. - -*/ --- DropForeignKey -ALTER TABLE "Tag" DROP CONSTRAINT "Tag_collectionId_fkey"; - --- AlterTable -ALTER TABLE "Tag" DROP COLUMN "collectionId"; diff --git a/prisma/migrations/20230327225044_init/migration.sql b/prisma/migrations/20230424185742_/migration.sql similarity index 90% rename from prisma/migrations/20230327225044_init/migration.sql rename to prisma/migrations/20230424185742_/migration.sql index b58779b..03e3bce 100644 --- a/prisma/migrations/20230327225044_init/migration.sql +++ b/prisma/migrations/20230424185742_/migration.sql @@ -13,6 +13,7 @@ CREATE TABLE "User" ( CREATE TABLE "Collection" ( "id" SERIAL NOT NULL, "name" TEXT NOT NULL, + "description" TEXT NOT NULL DEFAULT '', "ownerId" INTEGER NOT NULL, "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, @@ -48,7 +49,6 @@ CREATE TABLE "Link" ( CREATE TABLE "Tag" ( "id" SERIAL NOT NULL, "name" TEXT NOT NULL, - "collectionId" INTEGER NOT NULL, "ownerId" INTEGER NOT NULL, CONSTRAINT "Tag_pkey" PRIMARY KEY ("id") @@ -69,9 +69,6 @@ CREATE UNIQUE INDEX "Collection_name_ownerId_key" ON "Collection"("name", "owner -- CreateIndex CREATE UNIQUE INDEX "Tag_name_ownerId_key" ON "Tag"("name", "ownerId"); --- CreateIndex -CREATE UNIQUE INDEX "Tag_name_collectionId_key" ON "Tag"("name", "collectionId"); - -- CreateIndex CREATE UNIQUE INDEX "_LinkToTag_AB_unique" ON "_LinkToTag"("A", "B"); @@ -90,9 +87,6 @@ ALTER TABLE "UsersAndCollections" ADD CONSTRAINT "UsersAndCollections_collection -- AddForeignKey ALTER TABLE "Link" ADD CONSTRAINT "Link_collectionId_fkey" FOREIGN KEY ("collectionId") REFERENCES "Collection"("id") ON DELETE RESTRICT ON UPDATE CASCADE; --- AddForeignKey -ALTER TABLE "Tag" ADD CONSTRAINT "Tag_collectionId_fkey" FOREIGN KEY ("collectionId") REFERENCES "Collection"("id") ON DELETE RESTRICT ON UPDATE CASCADE; - -- AddForeignKey ALTER TABLE "Tag" ADD CONSTRAINT "Tag_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 96f1952..b4a5fef 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -26,6 +26,7 @@ model User { model Collection { id Int @id @default(autoincrement()) name String + description String @default("") owner User @relation(fields: [ownerId], references: [id]) ownerId Int members UsersAndCollections[] diff --git a/store/collections.ts b/store/collections.ts index 01d6320..a629aa8 100644 --- a/store/collections.ts +++ b/store/collections.ts @@ -5,11 +5,12 @@ import { create } from "zustand"; import { Collection } from "@prisma/client"; +import { NewCollection } from "@/types/global"; type CollectionStore = { collections: Collection[]; setCollections: () => void; - addCollection: (collectionName: string) => void; + addCollection: (body: NewCollection) => Promise; updateCollection: (collection: Collection) => void; removeCollection: (collectionId: number) => void; }; @@ -23,9 +24,9 @@ const useCollectionStore = create()((set) => ({ if (response.ok) set({ collections: data.response }); }, - addCollection: async (collectionName) => { + addCollection: async (body) => { const response = await fetch("/api/routes/collections", { - body: JSON.stringify({ collectionName }), + body: JSON.stringify(body), headers: { "Content-Type": "application/json", }, @@ -38,6 +39,8 @@ const useCollectionStore = create()((set) => ({ set((state) => ({ collections: [...state.collections, data.response], })); + + return response.ok; }, updateCollection: (collection) => set((state) => ({ diff --git a/store/links.ts b/store/links.ts index fb8469c..35d57b1 100644 --- a/store/links.ts +++ b/store/links.ts @@ -4,14 +4,14 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . import { create } from "zustand"; -import { ExtendedLink } from "@/types/global"; +import { ExtendedLink, NewLink } from "@/types/global"; import useTagStore from "./tags"; import useCollectionStore from "./collections"; type LinkStore = { links: ExtendedLink[]; setLinks: () => void; - addLink: (linkName: ExtendedLink) => Promise; + addLink: (body: NewLink) => Promise; updateLink: (link: ExtendedLink) => void; removeLink: (link: ExtendedLink) => void; }; @@ -25,9 +25,9 @@ const useLinkStore = create()((set) => ({ if (response.ok) set({ links: data.response }); }, - addLink: async (newLink) => { + addLink: async (body) => { const response = await fetch("/api/routes/links", { - body: JSON.stringify(newLink), + body: JSON.stringify(body), headers: { "Content-Type": "application/json", }, diff --git a/types/global.ts b/types/global.ts index 43cffd5..d352910 100644 --- a/types/global.ts +++ b/types/global.ts @@ -20,3 +20,8 @@ export interface NewLink { ownerId: number | undefined; }; } + +export interface NewCollection { + name: string; + description: string; +}