diff --git a/components/ClickAwayHandler.tsx b/components/ClickAwayHandler.tsx
index dec4c934..0da01378 100644
--- a/components/ClickAwayHandler.tsx
+++ b/components/ClickAwayHandler.tsx
@@ -3,6 +3,7 @@ import React, { useRef, useEffect, ReactNode, RefObject } from "react";
interface Props {
children: ReactNode;
onClickOutside: Function;
+ className?: string;
}
function useOutsideAlerter(
@@ -27,9 +28,17 @@ function useOutsideAlerter(
}, [ref, onClickOutside]);
}
-export default function OutsideAlerter({ children, onClickOutside }: Props) {
+export default function OutsideAlerter({
+ children,
+ onClickOutside,
+ className,
+}: Props) {
const wrapperRef = useRef(null);
useOutsideAlerter(wrapperRef, onClickOutside);
- return
{children}
;
+ return (
+
+ {children}
+
+ );
}
diff --git a/components/CollectionCards.tsx b/components/CollectionCards.tsx
new file mode 100644
index 00000000..250a5769
--- /dev/null
+++ b/components/CollectionCards.tsx
@@ -0,0 +1,32 @@
+import useCollectionSlice from "@/store/collection";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faChevronRight } from "@fortawesome/free-solid-svg-icons";
+
+export default function Collections() {
+ const { collections } = useCollectionSlice();
+
+ return (
+
+ {collections.map((e, i) => {
+ const formattedDate = new Date(e.createdAt).toLocaleString("en-US", {
+ year: "numeric",
+ month: "short",
+ day: "numeric",
+ });
+
+ return (
+
+ );
+ })}
+
+ );
+}
diff --git a/components/Collections.tsx b/components/Collections.tsx
deleted file mode 100644
index 0cf0a2b9..00000000
--- a/components/Collections.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useEffect, useState } from "react";
-
-interface Collections {
- id: number;
- name: string;
- createdAt: string;
-}
-
-export default function Collections() {
- const [collections, setCollections] = useState([]);
- useEffect(() => {
- fetch("/api/routes/collections/getCollections")
- .then((res) => res.json())
- .then((data) => setCollections(data.response));
- }, []);
-
- return (
-
- {collections.map((e, i) => {
- return (
-
- );
- })}
-
- );
-}
diff --git a/components/Navbar.tsx b/components/Navbar.tsx
index cc7659fa..26d52671 100644
--- a/components/Navbar.tsx
+++ b/components/Navbar.tsx
@@ -2,7 +2,7 @@ import { signOut } from "next-auth/react";
export default function Navbar() {
return (
-
+
Navbar
signOut()} className="cursor-pointer w-max">
Sign Out
diff --git a/components/Sidebar.tsx b/components/Sidebar.tsx
index 36caab4b..8892b400 100644
--- a/components/Sidebar.tsx
+++ b/components/Sidebar.tsx
@@ -1,16 +1,26 @@
import { useSession } from "next-auth/react";
import ClickAwayHandler from "@/components/ClickAwayHandler";
import { useState } from "react";
+import useCollectionSlice from "@/store/collection";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faFolder, faUserCircle } from "@fortawesome/free-regular-svg-icons";
+import {
+ faDatabase,
+ faPlus,
+ faChevronDown,
+} from "@fortawesome/free-solid-svg-icons";
export default function Sidebar() {
- const { data: session, status } = useSession();
+ const { data: session } = useSession();
- const [addCollection, setAddCollection] = useState(false);
+ const [collectionInput, setCollectionInput] = useState(false);
+
+ const { collections, addCollection } = useCollectionSlice();
const user = session?.user;
- const toggleAddCollection = () => {
- setAddCollection(!addCollection);
+ const toggleCollectionInput = () => {
+ setCollectionInput(!collectionInput);
};
const submitCollection = async (
@@ -19,42 +29,62 @@ export default function Sidebar() {
const collectionName: string = (event.target as HTMLInputElement).value;
if (event.key === "Enter" && collectionName) {
- await fetch("/api/routes/collections/postCollection", {
- body: JSON.stringify({ collectionName }),
- headers: {
- "Content-Type": "application/json",
- },
- method: "POST",
- })
- .then((res) => res.json())
- .then((data) => console.log(data));
+ addCollection(collectionName);
+ (event.target as HTMLInputElement).value = "";
}
};
return (
-
-
-
{user?.name}
+
+
- {addCollection ? (
-
+
+
+
+
Collections
+ {collectionInput ? (
+
) : (
-
- Create Collection +
-
+
)}
+
+ {collections.map((e, i) => {
+ return (
+
+ );
+ })}
+
);
}
diff --git a/Layouts/MainLayout.tsx b/layouts/MainLayout.tsx
similarity index 94%
rename from Layouts/MainLayout.tsx
rename to layouts/MainLayout.tsx
index 1e9c9243..e564a658 100644
--- a/Layouts/MainLayout.tsx
+++ b/layouts/MainLayout.tsx
@@ -6,6 +6,7 @@ import { useSession } from "next-auth/react";
import Loader from "../components/Loader";
import useRedirection from "@/hooks/useRedirection";
import { useRouter } from "next/router";
+import getInitialData from "@/lib/client/getInitialData";
interface Props {
children: ReactNode;
@@ -14,11 +15,11 @@ interface Props {
export default function Layout({ children }: Props) {
const { status } = useSession();
const router = useRouter();
-
const redirection = useRedirection();
-
const routeExists = router.route === "/_error" ? false : true;
+ getInitialData();
+
if (status === "authenticated" && !redirection && routeExists)
return (
<>
diff --git a/lib/api/controllers/collections/getCollections.ts b/lib/api/controllers/collections/getCollections.ts
new file mode 100644
index 00000000..acfb08d2
--- /dev/null
+++ b/lib/api/controllers/collections/getCollections.ts
@@ -0,0 +1,19 @@
+import type { NextApiRequest, NextApiResponse } from "next";
+import { prisma } from "@/lib/api/db";
+import { Session } from "next-auth";
+
+export default async function handler(
+ req: NextApiRequest,
+ res: NextApiResponse,
+ session: Session
+) {
+ const collections = await prisma.collection.findMany({
+ where: {
+ ownerId: session?.user.id,
+ },
+ });
+
+ return res.status(200).json({
+ response: collections || [],
+ });
+}
diff --git a/pages/api/routes/collections/postCollection.ts b/lib/api/controllers/collections/postCollection.ts
similarity index 52%
rename from pages/api/routes/collections/postCollection.ts
rename to lib/api/controllers/collections/postCollection.ts
index 61fc9289..22db6f79 100644
--- a/pages/api/routes/collections/postCollection.ts
+++ b/lib/api/controllers/collections/postCollection.ts
@@ -1,18 +1,12 @@
import type { NextApiRequest, NextApiResponse } from "next";
-import { getServerSession } from "next-auth/next";
-import { authOptions } from "pages/api/auth/[...nextauth]";
-import { prisma } from "@/lib/db";
-
-type Data = {
- response: object[] | string;
-};
+import { prisma } from "@/lib/api/db";
+import { Session } from "next-auth";
export default async function handler(
req: NextApiRequest,
- res: NextApiResponse
+ res: NextApiResponse,
+ session: Session
) {
- const session = await getServerSession(req, res, authOptions);
-
if (!session?.user?.email) {
return res.status(401).json({ response: "You must be logged in." });
}
@@ -39,44 +33,24 @@ export default async function handler(
},
});
- console.log(typeof session.user.id);
-
const checkIfCollectionExists = findCollection?.collections[0];
if (checkIfCollectionExists) {
return res.status(400).json({ response: "Collection already exists." });
}
- // const a = await prisma.user.update({
- // where: {
- // id: session.user.id,
- // },
- // data: {
- // // collections: {
- // // create: { name: "Das" },
- // // },
- // },
- // include: {
- // collections: { include: { collection: true } },
- // },
- // });
-
- await prisma.user.update({
- where: {
- id: session.user.id,
- },
+ const createCollection = await prisma.collection.create({
data: {
- collections: {
- create: [
- {
- name: collectionName,
- },
- ],
+ owner: {
+ connect: {
+ id: session.user.id,
+ },
},
+ name: collectionName,
},
});
return res.status(200).json({
- response: "Success",
+ response: createCollection,
});
}
diff --git a/lib/db.ts b/lib/api/db.ts
similarity index 100%
rename from lib/db.ts
rename to lib/api/db.ts
diff --git a/lib/client/getInitialData.ts b/lib/client/getInitialData.ts
new file mode 100644
index 00000000..61a08ec7
--- /dev/null
+++ b/lib/client/getInitialData.ts
@@ -0,0 +1,14 @@
+import useCollectionSlice from "@/store/collection";
+import { useEffect } from "react";
+import { useSession } from "next-auth/react";
+
+export default function getInitialData() {
+ const { status } = useSession();
+ const { setCollections } = useCollectionSlice();
+
+ useEffect(() => {
+ if (status === "authenticated") {
+ setCollections();
+ }
+ }, [status]);
+}
diff --git a/package.json b/package.json
index aff7d38f..1832903b 100644
--- a/package.json
+++ b/package.json
@@ -13,6 +13,10 @@
"lint": "next lint"
},
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^6.3.0",
+ "@fortawesome/free-regular-svg-icons": "^6.3.0",
+ "@fortawesome/free-solid-svg-icons": "^6.3.0",
+ "@fortawesome/react-fontawesome": "^0.2.0",
"@next/font": "13.1.6",
"@prisma/client": "^4.9.0",
"@types/node": "18.11.18",
@@ -25,7 +29,8 @@
"next-auth": "^4.19.1",
"react": "18.2.0",
"react-dom": "18.2.0",
- "typescript": "4.9.4"
+ "typescript": "4.9.4",
+ "zustand": "^4.3.3"
},
"devDependencies": {
"@types/bcrypt": "^5.0.0",
diff --git a/pages/_app.tsx b/pages/_app.tsx
index 70ecb537..aff7668c 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -1,4 +1,5 @@
-import MainLayout from "@/Layouts/MainLayout";
+import React from "react";
+import MainLayout from "@/layouts/MainLayout";
import "@/styles/globals.css";
import { SessionProvider } from "next-auth/react";
import type { AppProps } from "next/app";
diff --git a/pages/api/auth/[...nextauth].ts b/pages/api/auth/[...nextauth].ts
index 2b9b237d..804774f1 100644
--- a/pages/api/auth/[...nextauth].ts
+++ b/pages/api/auth/[...nextauth].ts
@@ -1,4 +1,4 @@
-import { prisma } from "@/lib/db";
+import { prisma } from "@/lib/api/db";
import NextAuth from "next-auth/next";
import CredentialsProvider from "next-auth/providers/credentials";
import { AuthOptions } from "next-auth";
diff --git a/pages/api/auth/register.ts b/pages/api/auth/register.ts
index 2c79af10..0677bda9 100644
--- a/pages/api/auth/register.ts
+++ b/pages/api/auth/register.ts
@@ -1,4 +1,4 @@
-import { prisma } from "@/lib/db";
+import { prisma } from "@/lib/api/db";
import type { NextApiRequest, NextApiResponse } from "next";
import bcrypt from "bcrypt";
diff --git a/pages/api/routes/collections/getCollections.ts b/pages/api/routes/collections/index.ts
similarity index 51%
rename from pages/api/routes/collections/getCollections.ts
rename to pages/api/routes/collections/index.ts
index 6ab39d4e..b8a102af 100644
--- a/pages/api/routes/collections/getCollections.ts
+++ b/pages/api/routes/collections/index.ts
@@ -1,7 +1,8 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { getServerSession } from "next-auth/next";
import { authOptions } from "pages/api/auth/[...nextauth]";
-import { prisma } from "@/lib/db";
+import getCollections from "@/lib/api/controllers/collections/getCollections";
+import postCollections from "@/lib/api/controllers/collections/postCollection";
type Data = {
response: object[] | string;
@@ -17,24 +18,7 @@ export default async function handler(
return res.status(401).json({ response: "You must be logged in." });
}
- const email: string = session.user.email;
+ if (req.method === "GET") return await getCollections(req, res, session);
- const findCollection = await prisma.user.findFirst({
- where: {
- email: email,
- },
- include: {
- collections: true,
- },
- });
-
- const collections = findCollection?.collections.map((e) => {
- return { id: e.id, name: e.name, createdAt: e.createdAt };
- });
-
- // console.log(session?.user?.email);
-
- return res.status(200).json({
- response: collections || [],
- });
+ if (req.method === "POST") return await postCollections(req, res, session);
}
diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx
index 6a953c9a..a8f97a72 100644
--- a/pages/dashboard.tsx
+++ b/pages/dashboard.tsx
@@ -1,5 +1,5 @@
import { useSession } from "next-auth/react";
-import Collections from "@/components/Collections";
+import CollectionCards from "@/components/CollectionCards";
export default function Dashboard() {
const { data: session, status } = useSession();
@@ -7,9 +7,9 @@ export default function Dashboard() {
const user = session?.user;
return (
+ // ml-80
);
}
diff --git a/store/collection.ts b/store/collection.ts
new file mode 100644
index 00000000..f2c23ef0
--- /dev/null
+++ b/store/collection.ts
@@ -0,0 +1,65 @@
+import { create } from "zustand";
+import { Collection } from "@prisma/client";
+
+type CollectionSlice = {
+ collections: Collection[];
+ setCollections: () => void;
+ addCollection: (collectionName: string) => void;
+ updateCollection: (collection: Collection) => void;
+ removeCollection: (collectionId: number) => void;
+};
+
+const useCollectionSlice = create()((set) => ({
+ collections: [],
+ setCollections: async () => {
+ const response = await fetch("/api/routes/collections");
+
+ const data = await response.json();
+
+ if (response.ok) set({ collections: data.response });
+ },
+ addCollection: async (collectionName) => {
+ const response = await fetch("/api/routes/collections", {
+ body: JSON.stringify({ collectionName }),
+ headers: {
+ "Content-Type": "application/json",
+ },
+ method: "POST",
+ });
+
+ const data = await response.json();
+
+ if (response.ok)
+ set((state) => ({
+ collections: [...state.collections, data.response],
+ }));
+ },
+ updateCollection: (collection) =>
+ set((state) => ({
+ collections: state.collections.map((c) =>
+ c.id === collection.id ? collection : c
+ ),
+ })),
+ removeCollection: (collectionId) => {
+ // await fetch("/api/routes/collections/postCollection", {
+ // body: JSON.stringify({ collectionName }),
+ // headers: {
+ // "Content-Type": "application/json",
+ // },
+ // method: "POST",
+ // })
+ // .then((res) => res.json())
+ // .then((data) => {
+ // console.log(data);
+ // set((state) => ({
+ // collections: [...state.collections, data.response],
+ // }));
+ // });
+
+ set((state) => ({
+ collections: state.collections.filter((c) => c.id !== collectionId),
+ }));
+ },
+}));
+
+export default useCollectionSlice;
diff --git a/styles/globals.css b/styles/globals.css
index b5c61c95..edf4f200 100644
--- a/styles/globals.css
+++ b/styles/globals.css
@@ -1,3 +1,12 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
+
+/* Hide scrollbar */
+.hidw-scrollbar::-webkit-scrollbar {
+ display: none;
+}
+.hide-scrollbar {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+}
diff --git a/yarn.lock b/yarn.lock
index 51cd4ecf..168f90c6 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -24,6 +24,39 @@
minimatch "^3.1.2"
strip-json-comments "^3.1.1"
+"@fortawesome/fontawesome-common-types@6.3.0":
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.3.0.tgz#51f734e64511dbc3674cd347044d02f4dd26e86b"
+ integrity sha512-4BC1NMoacEBzSXRwKjZ/X/gmnbp/HU5Qqat7E8xqorUtBFZS+bwfGH5/wqOC2K6GV0rgEobp3OjGRMa5fK9pFg==
+
+"@fortawesome/fontawesome-svg-core@^6.3.0":
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.3.0.tgz#b6a17d48d231ac1fad93e43fca7271676bf316cf"
+ integrity sha512-uz9YifyKlixV6AcKlOX8WNdtF7l6nakGyLYxYaCa823bEBqyj/U2ssqtctO38itNEwXb8/lMzjdoJ+aaJuOdrw==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "6.3.0"
+
+"@fortawesome/free-regular-svg-icons@^6.3.0":
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.3.0.tgz#286f87f777e6c96af59151e86647c81083029ee2"
+ integrity sha512-cZnwiVHZ51SVzWHOaNCIA+u9wevZjCuAGSvSYpNlm6A4H4Vhwh8481Bf/5rwheIC3fFKlgXxLKaw8Xeroz8Ntg==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "6.3.0"
+
+"@fortawesome/free-solid-svg-icons@^6.3.0":
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.3.0.tgz#d3bd33ae18bb15fdfc3ca136e2fea05f32768a65"
+ integrity sha512-x5tMwzF2lTH8pyv8yeZRodItP2IVlzzmBuD1M7BjawWgg9XAvktqJJ91Qjgoaf8qJpHQ8FEU9VxRfOkLhh86QA==
+ dependencies:
+ "@fortawesome/fontawesome-common-types" "6.3.0"
+
+"@fortawesome/react-fontawesome@^0.2.0":
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz#d90dd8a9211830b4e3c08e94b63a0ba7291ddcf4"
+ integrity sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==
+ dependencies:
+ prop-types "^15.8.1"
+
"@humanwhocodes/config-array@^0.11.8":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.8.tgz#03595ac2075a4dc0f191cc2131de14fbd7d410b9"
@@ -2605,6 +2638,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+use-sync-external-store@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
+ integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
+
util-deprecate@^1.0.1, util-deprecate@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -2704,3 +2742,10 @@ yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
+
+zustand@^4.3.3:
+ version "4.3.3"
+ resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.3.3.tgz#c9113499074dde2d6d99c1b5f591e9329572c224"
+ integrity sha512-x2jXq8S0kfLGNwGh87nhRfEc2eZy37tSatpSoSIN+O6HIaBhgQHSONV/F9VNrNcBcKQu/E80K1DeHDYQC/zCrQ==
+ dependencies:
+ use-sync-external-store "1.2.0"