diff --git a/components/Search.tsx b/components/Search.tsx index 3c5d3d9..a5dcac9 100644 --- a/components/Search.tsx +++ b/components/Search.tsx @@ -1,10 +1,13 @@ +// 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 { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useEffect, useState } from "react"; -import ClickAwayHandler from "./ClickAwayHandler"; import useSearchSettingsStore from "@/store/search"; import { useRouter } from "next/router"; -import Checkbox from "./Checkbox"; export default function Search() { const router = useRouter(); @@ -13,69 +16,47 @@ export default function Search() { false || router.pathname == "/search" ); - const { searchSettings, toggleCheckbox, setSearchQuery } = + const { searchSettings, setSearchSettings, setSearchQuery } = useSearchSettingsStore(); useEffect(() => { - if (router.pathname !== "/search") setSearchQuery(""); + if (router.pathname !== "/search") + setSearchSettings({ + query: "", + filter: { + name: true, + url: true, + title: true, + collection: true, + tags: true, + }, + }); }, [router]); return ( - setSearchBox(false)}> -
setSearchBox(true)} +
setSearchBox(true)} + > + - setSearchQuery(e.target.value)} - onFocus={() => router.push("/search")} - autoFocus={searchBox} - className="border border-sky-100 rounded-md pr-6 w-60 focus:border-sky-500 sm:focus:w-80 hover:border-sky-500 duration-100 outline-none p-1" - /> - {searchBox ? ( -
-
-

Filter by

- toggleCheckbox("name")} - /> - toggleCheckbox("url")} - /> - toggleCheckbox("title")} - /> - toggleCheckbox("collection")} - /> - toggleCheckbox("tags")} - /> -
-
- ) : null} -
- + { + setSearchQuery(e.target.value); + router.push("/search"); + }} + autoFocus={searchBox} + className="border border-sky-100 rounded-md pr-6 w-60 focus:border-sky-500 sm:focus:w-80 hover:border-sky-500 duration-100 outline-none p-1" + /> +
); } diff --git a/pages/search.tsx b/pages/search.tsx index 8bb25cb..2d448e1 100644 --- a/pages/search.tsx +++ b/pages/search.tsx @@ -3,53 +3,208 @@ // 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 Checkbox from "@/components/Checkbox"; +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 useSearchSettingsStore from "@/store/search"; -import { ExtendedLink } from "@/types/global"; -import { faSearch } from "@fortawesome/free-solid-svg-icons"; +import { faFilter, faSearch, faSort } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { useEffect, useState } from "react"; +import { ChangeEvent, useEffect, useState } from "react"; export default function Links() { const { links } = useLinkStore(); - const [filteredLinks, setFilteredLinks] = useState([]); + const [filterDropdown, setFilterDropdown] = useState(false); + const [sortDropdown, setSortDropdown] = useState(false); + const [sortBy, setSortBy] = useState("Name (A-Z)"); + const [sortedLinks, setSortedLinks] = useState(links); + const { searchSettings, toggleCheckbox, setSearchSettings, setSearchQuery } = + useSearchSettingsStore(); - const { searchSettings } = useSearchSettingsStore(); + const handleSortChange = (event: ChangeEvent) => { + setSortBy(event.target.value); + }; + + const { name, url, title, collection, tags } = searchSettings.filter; useEffect(() => { - const { name, url, title, collection, tags } = searchSettings.filter; + const linksArray = [ + ...links.filter((link) => { + const query = searchSettings.query.toLowerCase(); - const filter = links.filter((link) => { - const query = searchSettings.query.toLowerCase(); + if ( + (name && link.name.toLowerCase().includes(query)) || + (url && link.url.toLowerCase().includes(query)) || + (title && link.title.toLowerCase().includes(query)) || + (collection && link.collection.name.toLowerCase().includes(query)) || + (tags && + link.tags.some((tag) => tag.name.toLowerCase().includes(query))) + ) + return true; + }), + ]; - if ( - (name && link.name.toLowerCase().includes(query)) || - (url && link.url.toLowerCase().includes(query)) || - (title && link.title.toLowerCase().includes(query)) || - (collection && link.collection.name.toLowerCase().includes(query)) || - (tags && - link.tags.some((tag) => tag.name.toLowerCase().includes(query))) - ) - return true; - }); - - setFilteredLinks(filter); - }, [searchSettings, 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() + ) + ); + }, [searchSettings, links, sortBy]); return (
-
+

Search Results

+ +
+
+
setFilterDropdown(!filterDropdown)} + id="filter-dropdown" + className="inline-flex rounded-md cursor-pointer hover:bg-white hover:border-sky-500 border-sky-100 border duration-100 p-1" + > + +
+ + {filterDropdown ? ( + { + const target = e.target as HTMLInputElement; + if (target.id !== "filter-dropdown") + setFilterDropdown(false); + }} + className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-20 border border-sky-100 w-40" + > +

+ Filter by +

+
+ toggleCheckbox("name")} + /> + toggleCheckbox("url")} + /> + toggleCheckbox("title")} + /> + toggleCheckbox("collection")} + /> + toggleCheckbox("tags")} + /> +
+
+ ) : null} +
+ +
+
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} +
+
- {filteredLinks[0] ? ( - filteredLinks.map((e, i) => { + {sortedLinks[0] ? ( + sortedLinks.map((e, i) => { return ; }) ) : (