-
{count + 1}.
+
{count + 1}.
{link.name}
diff --git a/components/Sidebar/index.tsx b/components/Sidebar.tsx
similarity index 55%
rename from components/Sidebar/index.tsx
rename to components/Sidebar.tsx
index e644985..9e694bb 100644
--- a/components/Sidebar/index.tsx
+++ b/components/Sidebar.tsx
@@ -7,7 +7,6 @@ import {
faBookmark,
faChartSimple,
} from "@fortawesome/free-solid-svg-icons";
-import SidebarItem from "./SidebarItem";
import useTagStore from "@/store/tags";
import Link from "next/link";
import { useRouter } from "next/router";
@@ -23,13 +22,13 @@ export default function ({ className }: { className?: string }) {
useEffect(() => {
setActive(router.asPath);
- }, [router]);
+ }, [router, collections]);
return (
-
+
Linkwarden
@@ -38,45 +37,29 @@ export default function ({ className }: { className?: string }) {
-
- Dashboard
-
+
Dashboard
-
- All Collections
-
+
All Collections
@@ -84,60 +67,71 @@ export default function ({ className }: { className?: string }) {
-
- All Links
-
+
All Links
-
Collections
+
Collections
{collections
.sort((a, b) => a.name.localeCompare(b.name))
.map((e, i) => {
return (
-
}
- iconColor={e.color}
- path={`/collections/${e.id}`}
- className="capitalize"
- />
+
+
+
);
})}
{tags
.sort((a, b) => a.name.localeCompare(b.name))
.map((e, i) => {
return (
-
}
- path={`/tags/${e.id}`}
- />
+
+
+
);
})}
diff --git a/components/Sidebar/SidebarItem.tsx b/components/Sidebar/SidebarItem.tsx
deleted file mode 100644
index 926adc5..0000000
--- a/components/Sidebar/SidebarItem.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import Link from "next/link";
-import React, { ReactElement, useEffect, useState } from "react";
-import { useRouter } from "next/router";
-
-interface SidebarItemProps {
- text: string;
- icon: ReactElement;
- path: string;
- className?: string;
- iconColor?: string;
-}
-
-export default function ({
- text,
- icon,
- path,
- className,
- iconColor,
-}: SidebarItemProps) {
- const router = useRouter();
- const [active, setActive] = useState(false);
-
- useEffect(() => {
- if (router.asPath === path) setActive(true);
- else setActive(false);
- }, [router]);
-
- return (
-
-
- {React.cloneElement(icon, {
- className: "w-4 h-4",
- style: {
- color: active ? "white" : iconColor ? iconColor : "#7dd3fc",
- },
- })}
-
- {text}
-
-
-
- );
-}
diff --git a/components/SortLinkDropdown.tsx b/components/SortLinkDropdown.tsx
index 7c90cfe..fb94685 100644
--- a/components/SortLinkDropdown.tsx
+++ b/components/SortLinkDropdown.tsx
@@ -19,7 +19,7 @@ export default function SortLinkDropdown({
const target = e.target as HTMLInputElement;
if (target.id !== "sort-dropdown") toggleSortDropdown();
}}
- className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 border border-sky-100 w-48"
+ className="absolute top-8 right-0 shadow-md bg-gray-50 rounded-md p-2 z-10 w-48"
>
Sort by
diff --git a/lib/api/controllers/tags/getTags.ts b/lib/api/controllers/tags/getTags.ts
index b3bf070..0a3b484 100644
--- a/lib/api/controllers/tags/getTags.ts
+++ b/lib/api/controllers/tags/getTags.ts
@@ -4,6 +4,7 @@ export default async function (userId: number) {
// remove empty tags
await prisma.tag.deleteMany({
where: {
+ ownerId: userId,
links: {
none: {},
},
diff --git a/lib/api/db.ts b/lib/api/db.ts
index 8919c42..49e21fa 100644
--- a/lib/api/db.ts
+++ b/lib/api/db.ts
@@ -9,3 +9,10 @@ export const prisma =
});
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;
+
+if (process.env.NODE_ENV !== "production")
+ prisma.$on("query" as any, (e: any) => {
+ console.log("Query: " + e.query);
+ console.log("Params: " + e.params);
+ console.log("\x1b[31m", `Duration: ${e.duration}ms`, "\x1b[0m"); // For benchmarking
+ });
diff --git a/pages/api/avatar/[id].ts b/pages/api/avatar/[id].ts
index 1ff93aa..34b1e3d 100644
--- a/pages/api/avatar/[id].ts
+++ b/pages/api/avatar/[id].ts
@@ -46,7 +46,6 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
`data/uploads/avatar/${queryId}.jpg`
);
- console.log(filePath);
const file = fs.existsSync(filePath)
? fs.readFileSync(filePath)
: "File not found.";
diff --git a/pages/collections/index.tsx b/pages/collections/index.tsx
index 3e0450c..f81817b 100644
--- a/pages/collections/index.tsx
+++ b/pages/collections/index.tsx
@@ -85,7 +85,7 @@ export default function () {
All Collections
@@ -218,7 +218,7 @@ export default function () {
activeCollection={{
name: "",
description: "",
- color: "#7dd3fc",
+ color: "#0ea5e9",
isPublic: false,
ownerId: session.data?.user.id as number,
members: [],
diff --git a/pages/dashboard.tsx b/pages/dashboard.tsx
index ffe21c8..ba9ffca 100644
--- a/pages/dashboard.tsx
+++ b/pages/dashboard.tsx
@@ -39,7 +39,7 @@ export default function () {
Dashboard
diff --git a/pages/links.tsx b/pages/links.tsx
index b020775..0af609c 100644
--- a/pages/links.tsx
+++ b/pages/links.tsx
@@ -55,7 +55,7 @@ export default function Links() {
All Links
diff --git a/pages/search.tsx b/pages/search/[query].tsx
similarity index 70%
rename from pages/search.tsx
rename to pages/search/[query].tsx
index 2f2b54d..965e261 100644
--- a/pages/search.tsx
+++ b/pages/search/[query].tsx
@@ -1,7 +1,5 @@
-import Checkbox from "@/components/Checkbox";
-import ClickAwayHandler from "@/components/ClickAwayHandler";
+import FilterSearchDropdown from "@/components/FilterSearchDropdown";
import LinkList from "@/components/LinkList";
-import RadioButton from "@/components/RadioButton";
import SortLinkDropdown from "@/components/SortLinkDropdown";
import MainLayout from "@/layouts/MainLayout";
import useLinkStore from "@/store/links";
@@ -17,8 +15,7 @@ export default function Links() {
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, toggleCheckbox } = useSearchSettingsStore();
const handleSortChange = (event: ChangeEvent) => {
setSortBy(event.target.value);
@@ -93,45 +90,11 @@ export default function Links() {
{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}
diff --git a/pages/search/index.tsx b/pages/search/index.tsx
new file mode 100644
index 0000000..9277b63
--- /dev/null
+++ b/pages/search/index.tsx
@@ -0,0 +1,146 @@
+import FilterSearchDropdown from "@/components/FilterSearchDropdown";
+import LinkList from "@/components/LinkList";
+import SortLinkDropdown from "@/components/SortLinkDropdown";
+import MainLayout from "@/layouts/MainLayout";
+import useLinkStore from "@/store/links";
+import useSearchSettingsStore from "@/store/search";
+import { faFilter, faSearch, 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 [filterDropdown, setFilterDropdown] = useState(false);
+ const [sortDropdown, setSortDropdown] = useState(false);
+ const [sortBy, setSortBy] = useState("Name (A-Z)");
+ const [sortedLinks, setSortedLinks] = useState(links);
+ const { searchSettings, toggleCheckbox } = useSearchSettingsStore();
+
+ const handleSortChange = (event: ChangeEvent
) => {
+ setSortBy(event.target.value);
+ };
+
+ const { name, url, title, collection, tags } = searchSettings.filter;
+
+ useEffect(() => {
+ const linksArray = [
+ ...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 (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 as string).getTime() -
+ new Date(a.createdAt as string).getTime()
+ )
+ );
+ else if (sortBy === "Date (Oldest First)")
+ setSortedLinks(
+ linksArray.sort(
+ (a, b) =>
+ new Date(a.createdAt as string).getTime() -
+ new Date(b.createdAt as string).getTime()
+ )
+ );
+ }, [searchSettings, links, sortBy]);
+
+ return (
+
+
+
+
+
+
+
+
setFilterDropdown(!filterDropdown)}
+ id="filter-dropdown"
+ className="inline-flex rounded-md cursor-pointer hover:bg-slate-200 duration-100 p-1"
+ >
+
+
+
+ {filterDropdown ? (
+
+ ) : null}
+
+
+
+
setSortDropdown(!sortDropdown)}
+ id="sort-dropdown"
+ className="inline-flex rounded-md cursor-pointer hover:bg-slate-200 duration-100 p-1"
+ >
+
+
+
+ {sortDropdown ? (
+
setSortDropdown(!sortDropdown)}
+ />
+ ) : null}
+
+
+
+ {sortedLinks[0] ? (
+ sortedLinks.map((e, i) => {
+ return
;
+ })
+ ) : (
+
+ Nothing found.{" "}
+
+ ¯\_(ツ)_/¯
+
+
+ )}
+
+
+ );
+}
diff --git a/pages/tags/[id].tsx b/pages/tags/[id].tsx
index 57d677e..adea5c6 100644
--- a/pages/tags/[id].tsx
+++ b/pages/tags/[id].tsx
@@ -71,7 +71,7 @@ export default function () {
{activeTag?.name}
diff --git a/prisma/migrations/20230602115917_init/migration.sql b/prisma/migrations/20230605090235_init/migration.sql
similarity index 98%
rename from prisma/migrations/20230602115917_init/migration.sql
rename to prisma/migrations/20230605090235_init/migration.sql
index 4fffd6a..4c5fa84 100644
--- a/prisma/migrations/20230602115917_init/migration.sql
+++ b/prisma/migrations/20230605090235_init/migration.sql
@@ -16,7 +16,7 @@ CREATE TABLE "Collection" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"description" TEXT NOT NULL DEFAULT '',
- "color" TEXT NOT NULL DEFAULT '#7dd3fc',
+ "color" TEXT NOT NULL DEFAULT '#0ea5e9',
"isPublic" BOOLEAN NOT NULL DEFAULT false,
"ownerId" INTEGER NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index 636dd0d..2fc9cfd 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -24,7 +24,7 @@ model Collection {
id Int @id @default(autoincrement())
name String
description String @default("")
- color String @default("#7dd3fc")
+ color String @default("#0ea5e9")
isPublic Boolean @default(false)
owner User @relation(fields: [ownerId], references: [id])
ownerId Int