diff --git a/components/CollectionCard.tsx b/components/CollectionCard.tsx index a85a7ee..7b20782 100644 --- a/components/CollectionCard.tsx +++ b/components/CollectionCard.tsx @@ -5,10 +5,12 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faChevronRight } from "@fortawesome/free-solid-svg-icons"; -import { Collection } from "@prisma/client"; import Link from "next/link"; +import { ExtendedCollection } from "@/types/global"; +import useLinkStore from "@/store/links"; -export default function ({ collection }: { collection: Collection }) { +export default function ({ collection }: { collection: ExtendedCollection }) { + const { links } = useLinkStore(); const formattedDate = new Date(collection.createdAt).toLocaleString("en-US", { year: "numeric", month: "short", @@ -17,20 +19,37 @@ export default function ({ collection }: { collection: Collection }) { return ( -
+
-

{collection.name}

+

{collection.name}

-

- {collection.description} +

{collection.description}

+
+
+

Members:

+ {collection.members.map((e, i) => { + return ( +

+ {e.user.name} +

+ ); + })} +
+
+

{formattedDate}

+

+ {links.filter((e) => e.collectionId === collection.id).length} Links

-

{formattedDate}

); diff --git a/components/Dashboard/CollectionItem.tsx b/components/Dashboard/CollectionItem.tsx new file mode 100644 index 0000000..ded9cb5 --- /dev/null +++ b/components/Dashboard/CollectionItem.tsx @@ -0,0 +1,59 @@ +// Copyright (C) 2022-present Daniel31x13 +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// 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 { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { faChevronRight } from "@fortawesome/free-solid-svg-icons"; +import Link from "next/link"; +import { ExtendedCollection } from "@/types/global"; +import useLinkStore from "@/store/links"; + +export default function ({ collection }: { collection: ExtendedCollection }) { + const { links } = useLinkStore(); + const formattedDate = new Date(collection.createdAt).toLocaleString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); + + return ( + +
+
+
+
+

{collection.name}

+

{collection.description}

+
+ + +
+
+
+

Members:

+ {collection.members.map((e, i) => { + return ( +

+ {e.user.name} +

+ ); + })} +
+
+

{formattedDate}

+

+ {links.filter((e) => e.collectionId === collection.id).length} Links +

+
+
+ + ); +} diff --git a/components/Dashboard/LinkItem.tsx b/components/Dashboard/LinkItem.tsx new file mode 100644 index 0000000..fd4c34c --- /dev/null +++ b/components/Dashboard/LinkItem.tsx @@ -0,0 +1,131 @@ +// Copyright (C) 2022-present Daniel31x13 +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// 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 { ExtendedLink } from "@/types/global"; +import { + faFolder, + faArrowUpRightFromSquare, +} from "@fortawesome/free-solid-svg-icons"; +import { faFileImage, faFilePdf } from "@fortawesome/free-regular-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { useState } from "react"; +import Image from "next/image"; +import Link from "next/link"; + +export default function ({ + link, + count, +}: { + link: ExtendedLink; + count: number; +}) { + const [editModal, setEditModal] = useState(false); + + const url = new URL(link.url); + const formattedDate = new Date(link.createdAt).toLocaleString("en-US", { + year: "numeric", + month: "short", + day: "numeric", + }); + + return ( +
+ { + const target = e.target as HTMLElement; + target.style.opacity = "0"; + }} + /> + { + const target = e.target as HTMLElement; + target.style.opacity = "0"; + }} + /> +
+
+
+

{count + 1}.

+

{link.name}

+
+

{link.title}

+
+ +
+ +

{link.collection.name}

+
+ + +
+ {link.tags.map((e, i) => ( + +

+ # {e.name} +

+ + ))} +
+
+
+

{formattedDate}

+ +
+

{url.host}

+ +
+
+
+
+ +
+ + + + + + + +
+
+
+ ); +} diff --git a/components/LinkList.tsx b/components/LinkList.tsx index a666a7e..1445a5c 100644 --- a/components/LinkList.tsx +++ b/components/LinkList.tsx @@ -144,6 +144,7 @@ export default function ({ )}`} onMouseEnter={() => setArchiveLabel("Screenshot")} target="_blank" + title="Screenshot" > setArchiveLabel("PDF")} + title="PDF" > { + if (router.pathname !== "/search") setSearchQuery(""); + }, [router]); + return ( setSearchBox(false)}>
+ +
+ +

+ Dashboard +

+
+ +
+
@@ -146,6 +146,6 @@ export default function () { return ; })}
- + ); } diff --git a/pages/collections/index.tsx b/pages/collections/index.tsx index 080e7c1..45dd254 100644 --- a/pages/collections/index.tsx +++ b/pages/collections/index.tsx @@ -7,75 +7,162 @@ import useCollectionStore from "@/store/collections"; import { faAdd, faBox, + faCheck, faEllipsis, faPlus, + faSort, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import CollectionCard from "@/components/CollectionCard"; import Dropdown from "@/components/Dropdown"; -import { useState } from "react"; +import { ChangeEvent, useState } from "react"; import Modal from "@/components/Modal"; import AddCollection from "@/components/Modal/AddCollection"; -import Dashboard from "@/layouts/Dashboard"; +import MainLayout from "@/layouts/MainLayout"; +import ClickAwayHandler from "@/components/ClickAwayHandler"; export default function () { const { collections } = useCollectionStore(); const [expandDropdown, setExpandDropdown] = useState(false); + const [sortDropdown, setSortDropdown] = useState(false); - const [linkModal, setLinkModal] = useState(false); + const [collectionModal, setCollectionModal] = useState(false); const toggleCollectionModal = () => { - setLinkModal(!linkModal); + setCollectionModal(!collectionModal); + }; + + const [sortBy, setSortBy] = useState(""); + + const handleSortChange = (event: ChangeEvent) => { + setSortBy(event.target.value); }; return ( // ml-80 - +
-
-
- -

All Collections

+
+
+
+ +

All Collections

+
+
+
setExpandDropdown(!expandDropdown)} + id="edit-dropdown" + className="inline-flex rounded-md cursor-pointer hover:bg-white hover:border-sky-500 border-sky-100 border duration-100 p-1" + > + +
+ + {expandDropdown ? ( + , + onClick: () => { + toggleCollectionModal(); + setExpandDropdown(false); + }, + }, + ]} + onClickOutside={(e: Event) => { + const target = e.target as HTMLInputElement; + if (target.id !== "edit-dropdown") setExpandDropdown(false); + }} + className="absolute top-8 left-0 w-36" + /> + ) : null} +
+
setExpandDropdown(!expandDropdown)} - id="edit-dropdown" + onClick={() => setSortDropdown(!sortDropdown)} + id="sort-dropdown" className="inline-flex rounded-md cursor-pointer hover:bg-white hover:border-sky-500 border-sky-100 border duration-100 p-1" >
- {expandDropdown ? ( - , - onClick: () => { - toggleCollectionModal(); - setExpandDropdown(false); - }, - }, - ]} + + {sortDropdown ? ( + { const target = e.target as HTMLInputElement; - if (target.id !== "edit-dropdown") setExpandDropdown(false); + if (target.id !== "sort-dropdown") setSortDropdown(false); }} - className="absolute top-8 left-0 w-36" - /> + className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-36" + > +

Sort by

+
+ + + +
+
) : null}
- - {linkModal ? ( - - - - ) : null}
+
{collections.map((e, i) => { return ; @@ -90,6 +177,12 @@ export default function () {
- + + {collectionModal ? ( + + + + ) : null} + ); } diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx new file mode 100644 index 0000000..393ca83 --- /dev/null +++ b/pages/dashboard.tsx @@ -0,0 +1,117 @@ +// Copyright (C) 2022-present Daniel31x13 +// This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 3. +// 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 useCollectionStore from "@/store/collections"; +import { faArrowRight, faChartSimple } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import MainLayout from "@/layouts/MainLayout"; +import useLinkStore from "@/store/links"; +import useTagStore from "@/store/tags"; +import LinkItem from "@/components/Dashboard/LinkItem"; +import Link from "next/link"; +import CollectionItem from "@/components/Dashboard/CollectionItem"; +import { useEffect, useState } from "react"; + +export default function () { + const { collections } = useCollectionStore(); + const { links } = useLinkStore(); + const { tags } = useTagStore(); + + const [sortedCollections, setSortedCollections] = useState([]); + + useEffect(() => { + const collectionsWithLinkCount = collections.map((collection) => { + const linkCount = links.filter( + (link) => link.collectionId === collection.id + ).length; + return { ...collection, linkCount }; + }); + + setSortedCollections( + collectionsWithLinkCount.sort((a, b) => b.linkCount - a.linkCount) as any + ); + }, [collections]); + + return ( + // ml-80 + +
+
+
+ +

Dashboard

+
+
+ +
+
+

{links.length}

+

Links

+
+ +
+

+ {collections.length} +

+

Collections

+
+ +
+

{tags.length}

+

Tags

+
+
+ +
+
+
+

Recently added Links

+ +
+ View All + +
+ +
+ {links + .sort( + (a, b) => + new Date(b.createdAt).getTime() - + new Date(a.createdAt).getTime() + ) + .slice(0, 5) + .map((e, i) => ( + + ))} +
+ +
+
+

Top Collections

+ +
+ View All + +
+ +
+ {sortedCollections.map((e, i) => ( + + ))} +
+
+
+
+ ); +} diff --git a/pages/index.tsx b/pages/index.tsx index c0c4a0f..d82e286 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -10,6 +10,6 @@ export default function Home() { const router = useRouter(); useEffect(() => { - router.push("/collections"); + router.push("/MainLayout"); }, []); } diff --git a/pages/links.tsx b/pages/links.tsx index 8923dc6..68ad350 100644 --- a/pages/links.tsx +++ b/pages/links.tsx @@ -4,7 +4,7 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . import LinkList from "@/components/LinkList"; -import Dashboard from "@/layouts/Dashboard"; +import MainLayout from "@/layouts/MainLayout"; import useLinkStore from "@/store/links"; import { faBookmark } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; @@ -13,7 +13,7 @@ export default function Links() { const { links } = useLinkStore(); return ( - +
@@ -28,6 +28,6 @@ export default function Links() { return ; })}
- + ); } diff --git a/pages/search.tsx b/pages/search.tsx index cbbfa7b..8bb25cb 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -4,7 +4,7 @@ // You should have received a copy of the GNU General Public License along with this program. If not, see . import LinkList from "@/components/LinkList"; -import Dashboard from "@/layouts/Dashboard"; +import MainLayout from "@/layouts/MainLayout"; import useLinkStore from "@/store/links"; import useSearchSettingsStore from "@/store/search"; import { ExtendedLink } from "@/types/global"; @@ -40,7 +40,7 @@ export default function Links() { }, [searchSettings, links]); return ( - +
@@ -48,12 +48,19 @@ export default function Links() {

Search Results

- {filteredLinks[0] - ? filteredLinks.map((e, i) => { - return ; - }) - : "No results..."} + {filteredLinks[0] ? ( + filteredLinks.map((e, i) => { + return ; + }) + ) : ( +

+ Nothing found.{" "} + + ¯\_(ツ)_/¯ + +

+ )}
-
+ ); } diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx index 4a06f14..70d0f87 100644 --- a/pages/tags/[id].tsx +++ b/pages/tags/[id].tsx @@ -3,7 +3,7 @@ // 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 Dashboard from "@/layouts/Dashboard"; +import MainLayout from "@/layouts/MainLayout"; import { useRouter } from "next/router"; export default function () { @@ -12,8 +12,8 @@ export default function () { const tagId = Number(router.query.id); return ( - +
{"HI"}
-
+ ); }