From 6cb806ad3ae47c7ffc89c97e027c228af0fb5fc7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 16 May 2023 00:15:06 +0330 Subject: [PATCH] fully implemented sorting functionality --- pages/collections/[id].tsx | 245 +++++++++++++++++++++++++----------- pages/collections/index.tsx | 80 ++++++++++-- pages/links.tsx | 109 +++++++++++++++- pages/tags/[id].tsx | 145 ++++++++++++++++++++- 4 files changed, 491 insertions(+), 88 deletions(-) diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index f0921b8..94f0436 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -11,18 +11,21 @@ import EditCollection from "@/components/Modal/EditCollection"; import DeleteCollection from "@/components/Modal/DeleteCollection"; import useCollectionStore from "@/store/collections"; import useLinkStore from "@/store/links"; -import { ExtendedCollection, ExtendedLink } from "@/types/global"; +import { ExtendedCollection } from "@/types/global"; import { faAdd, faEllipsis, faFolder, faPenToSquare, + faSort, faTrashCan, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useRouter } from "next/router"; -import { useEffect, useState } from "react"; +import { ChangeEvent, useEffect, useState } from "react"; import MainLayout from "@/layouts/MainLayout"; +import RadioButton from "@/components/RadioButton"; +import ClickAwayHandler from "@/components/ClickAwayHandler"; export default function () { const router = useRouter(); @@ -34,10 +37,13 @@ export default function () { const [linkModal, setLinkModal] = useState(false); const [editCollectionModal, setEditCollectionModal] = useState(false); const [deleteCollectionModal, setDeleteCollectionModal] = useState(false); + const [sortDropdown, setSortDropdown] = useState(false); + const [sortBy, setSortBy] = useState("Name (A-Z)"); + const [activeCollection, setActiveCollection] = useState(); - const [linksByCollection, setLinksByCollection] = - useState(links); + + const [sortedLinks, setSortedLinks] = useState(links); const toggleLinkModal = () => { setLinkModal(!linkModal); @@ -51,98 +57,197 @@ export default function () { setDeleteCollectionModal(!deleteCollectionModal); }; - useEffect(() => { - setLinksByCollection( - links.filter((e) => e.collection.id === Number(router.query.id)) - ); + const handleSortChange = (event: ChangeEvent) => { + setSortBy(event.target.value); + }; + useEffect(() => { setActiveCollection( collections.find((e) => e.id === Number(router.query.id)) ); - }, [links, router, collections]); + + // Sorting logic + + const linksArray = [ + ...links.filter((e) => e.collection.id === Number(router.query.id)), + ]; + + if (sortBy === "Name (A-Z)") + setSortedLinks(linksArray.sort((a, b) => a.name.localeCompare(b.name))); + else if (sortBy === "Title (A-Z)") + setSortedLinks(linksArray.sort((a, b) => a.title.localeCompare(b.title))); + else if (sortBy === "Name (Z-A)") + setSortedLinks(linksArray.sort((a, b) => b.name.localeCompare(a.name))); + else if (sortBy === "Title (Z-A)") + setSortedLinks(linksArray.sort((a, b) => b.title.localeCompare(a.title))); + else if (sortBy === "Date (Newest First)") + setSortedLinks( + linksArray.sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ) + ); + else if (sortBy === "Date (Oldest First)") + setSortedLinks( + linksArray.sort( + (a, b) => + new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() + ) + ); + }, [links, router, collections, sortBy]); return (
-
-
- -

{activeCollection?.name}

+
+
+
+ +

{activeCollection?.name}

+
+
+
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: () => { + toggleLinkModal(); + setExpandDropdown(false); + }, + }, + { + name: "Edit Collection", + icon: , + onClick: () => { + toggleEditCollectionModal(); + setExpandDropdown(false); + }, + }, + { + name: "Delete Collection", + icon: , + onClick: () => { + toggleDeleteCollectionModal(); + setExpandDropdown(false); + }, + }, + ]} + onClickOutside={(e: Event) => { + const target = e.target as HTMLInputElement; + if (target.id !== "edit-dropdown") setExpandDropdown(false); + }} + className="absolute top-8 left-0 z-10 w-44" + /> + ) : null} + + {linkModal ? ( + + + + ) : null} + + {editCollectionModal && activeCollection ? ( + + + + ) : null} + + {deleteCollectionModal && activeCollection ? ( + + + + ) : 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: () => { - toggleLinkModal(); - setExpandDropdown(false); - }, - }, - { - name: "Edit Collection", - icon: , - onClick: () => { - toggleEditCollectionModal(); - setExpandDropdown(false); - }, - }, - { - name: "Delete Collection", - icon: , - onClick: () => { - toggleDeleteCollectionModal(); - 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 z-10 w-44" - /> - ) : null} + className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-48" + > +

+ Sort by +

+
+ - {linkModal ? ( - - - - ) : null} + - {editCollectionModal && activeCollection ? ( - - - - ) : null} + - {deleteCollectionModal && activeCollection ? ( - - - + + + + + +
+
) : null}
- {linksByCollection.map((e, i) => { + {sortedLinks.map((e, i) => { return ; })}
diff --git a/pages/collections/index.tsx b/pages/collections/index.tsx index a04b76e..b6bd6fc 100644 --- a/pages/collections/index.tsx +++ b/pages/collections/index.tsx @@ -14,18 +14,19 @@ import { import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import CollectionCard from "@/components/CollectionCard"; import Dropdown from "@/components/Dropdown"; -import { ChangeEvent, useState } from "react"; +import { ChangeEvent, useEffect, useState } from "react"; import Modal from "@/components/Modal"; import AddCollection from "@/components/Modal/AddCollection"; import MainLayout from "@/layouts/MainLayout"; import ClickAwayHandler from "@/components/ClickAwayHandler"; -import { faCircle, faCircleCheck } from "@fortawesome/free-regular-svg-icons"; import RadioButton from "@/components/RadioButton"; export default function () { const { collections } = useCollectionStore(); const [expandDropdown, setExpandDropdown] = useState(false); const [sortDropdown, setSortDropdown] = useState(false); + const [sortBy, setSortBy] = useState("Name (A-Z)"); + const [sortedCollections, setSortedCollections] = useState(collections); const [collectionModal, setCollectionModal] = useState(false); @@ -33,12 +34,49 @@ export default function () { setCollectionModal(!collectionModal); }; - const [sortBy, setSortBy] = useState("Name"); - const handleSortChange = (event: ChangeEvent) => { setSortBy(event.target.value); }; + useEffect(() => { + const collectionsArray = [...collections]; + + if (sortBy === "Name (A-Z)") + setSortedCollections( + collectionsArray.sort((a, b) => a.name.localeCompare(b.name)) + ); + else if (sortBy === "Description (A-Z)") + setSortedCollections( + collectionsArray.sort((a, b) => + a.description.localeCompare(b.description) + ) + ); + else if (sortBy === "Name (Z-A)") + setSortedCollections( + collectionsArray.sort((a, b) => b.name.localeCompare(a.name)) + ); + else if (sortBy === "Description (Z-A)") + setSortedCollections( + collectionsArray.sort((a, b) => + b.description.localeCompare(a.description) + ) + ); + else if (sortBy === "Date (Newest First)") + setSortedCollections( + collectionsArray.sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ) + ); + else if (sortBy === "Date (Oldest First)") + setSortedCollections( + collectionsArray.sort( + (a, b) => + new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() + ) + ); + }, [collections, sortBy]); + return ( // ml-80 @@ -103,27 +141,45 @@ export default function () { const target = e.target as HTMLInputElement; if (target.id !== "sort-dropdown") setSortDropdown(false); }} - className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-36" + className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-48" >

Sort by

+ + + + + +
@@ -133,7 +189,7 @@ export default function () {
- {collections.map((e, i) => { + {sortedCollections.map((e, i) => { return ; })} diff --git a/pages/links.tsx b/pages/links.tsx index 68ad350..fa13629 100644 --- a/pages/links.tsx +++ b/pages/links.tsx @@ -3,19 +3,57 @@ // 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 LinkList from "@/components/LinkList"; +import RadioButton from "@/components/RadioButton"; import MainLayout from "@/layouts/MainLayout"; import useLinkStore from "@/store/links"; -import { faBookmark } from "@fortawesome/free-solid-svg-icons"; +import { faBookmark, faSort } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { ChangeEvent, useEffect, useState } from "react"; export default function Links() { const { links } = useLinkStore(); + const [sortDropdown, setSortDropdown] = useState(false); + const [sortBy, setSortBy] = useState("Name (A-Z)"); + const [sortedLinks, setSortedLinks] = useState(links); + + const handleSortChange = (event: ChangeEvent) => { + setSortBy(event.target.value); + }; + + useEffect(() => { + const linksArray = [...links]; + + if (sortBy === "Name (A-Z)") + setSortedLinks(linksArray.sort((a, b) => a.name.localeCompare(b.name))); + else if (sortBy === "Title (A-Z)") + setSortedLinks(linksArray.sort((a, b) => a.title.localeCompare(b.title))); + else if (sortBy === "Name (Z-A)") + setSortedLinks(linksArray.sort((a, b) => b.name.localeCompare(a.name))); + else if (sortBy === "Title (Z-A)") + setSortedLinks(linksArray.sort((a, b) => b.title.localeCompare(a.title))); + else if (sortBy === "Date (Newest First)") + setSortedLinks( + linksArray.sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ) + ); + else if (sortBy === "Date (Oldest First)") + setSortedLinks( + linksArray.sort( + (a, b) => + new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() + ) + ); + }, [links, sortBy]); + return (
-
+

All Links

+ +
+
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" + > + +
+ + {sortDropdown ? ( + { + const target = e.target as HTMLInputElement; + if (target.id !== "sort-dropdown") setSortDropdown(false); + }} + className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-48" + > +

+ Sort by +

+
+ + + + + + + + + + + +
+
+ ) : null} +
- {links.map((e, i) => { + {sortedLinks.map((e, i) => { return ; })}
diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx index 70d0f87..f1e14af 100644 --- a/pages/tags/[id].tsx +++ b/pages/tags/[id].tsx @@ -3,17 +3,156 @@ // 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 MainLayout from "@/layouts/MainLayout"; +import LinkList from "@/components/LinkList"; +import useLinkStore from "@/store/links"; +import { faHashtag, faSort } from "@fortawesome/free-solid-svg-icons"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useRouter } from "next/router"; +import { ChangeEvent, useEffect, useState } from "react"; +import MainLayout from "@/layouts/MainLayout"; +import RadioButton from "@/components/RadioButton"; +import ClickAwayHandler from "@/components/ClickAwayHandler"; +import { Tag } from "@prisma/client"; +import useTagStore from "@/store/tags"; export default function () { const router = useRouter(); - const tagId = Number(router.query.id); + const { links } = useLinkStore(); + const { tags } = useTagStore(); + + const [linkModal, setLinkModal] = useState(false); + const [editCollectionModal, setEditCollectionModal] = useState(false); + const [deleteCollectionModal, setDeleteCollectionModal] = useState(false); + const [sortDropdown, setSortDropdown] = useState(false); + const [sortBy, setSortBy] = useState("Name (A-Z)"); + + const [activeTag, setActiveTag] = useState(); + + const [sortedLinks, setSortedLinks] = useState(links); + + const handleSortChange = (event: ChangeEvent) => { + setSortBy(event.target.value); + }; + + useEffect(() => { + setActiveTag(tags.find((e) => e.id === Number(router.query.id))); + + // Sorting logic + + const linksArray = [ + ...links.filter((e) => + e.tags.some((e) => e.id === Number(router.query.id)) + ), + ]; + + if (sortBy === "Name (A-Z)") + setSortedLinks(linksArray.sort((a, b) => a.name.localeCompare(b.name))); + else if (sortBy === "Title (A-Z)") + setSortedLinks(linksArray.sort((a, b) => a.title.localeCompare(b.title))); + else if (sortBy === "Name (Z-A)") + setSortedLinks(linksArray.sort((a, b) => b.name.localeCompare(a.name))); + else if (sortBy === "Title (Z-A)") + setSortedLinks(linksArray.sort((a, b) => b.title.localeCompare(a.title))); + else if (sortBy === "Date (Newest First)") + setSortedLinks( + linksArray.sort( + (a, b) => + new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() + ) + ); + else if (sortBy === "Date (Oldest First)") + setSortedLinks( + linksArray.sort( + (a, b) => + new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime() + ) + ); + }, [links, router, tags, sortBy]); return ( -
{"HI"}
+
+
+
+
+ +

{activeTag?.name}

+
+
+ +
+
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" + > + +
+ + {sortDropdown ? ( + { + const target = e.target as HTMLInputElement; + if (target.id !== "sort-dropdown") setSortDropdown(false); + }} + className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-48" + > +

+ Sort by +

+
+ + + + + + + + + + + +
+
+ ) : null} +
+
+ {sortedLinks.map((e, i) => { + return ; + })} +
); }