many visual changes and improvements
This commit is contained in:
parent
6a4f21fc0a
commit
a9d7303359
|
@ -85,7 +85,7 @@ export default function ({ link, count }: Props) {
|
||||||
<div className="flex gap-3 items-center flex-wrap my-3">
|
<div className="flex gap-3 items-center flex-wrap my-3">
|
||||||
<Link href={`/collections/${link.collection.id}`}>
|
<Link href={`/collections/${link.collection.id}`}>
|
||||||
<div className="flex items-center gap-1 cursor-pointer hover:opacity-60 duration-100">
|
<div className="flex items-center gap-1 cursor-pointer hover:opacity-60 duration-100">
|
||||||
<FontAwesomeIcon icon={faFolder} className="w-4 text-sky-300" />
|
<FontAwesomeIcon icon={faFolder} className="w-4 text-sky-500" />
|
||||||
<p className="text-sky-900">{link.collection.name}</p>
|
<p className="text-sky-900">{link.collection.name}</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -24,7 +24,7 @@ export default function Dropdown({ onClickOutside, className, items }: Props) {
|
||||||
return (
|
return (
|
||||||
<ClickAwayHandler
|
<ClickAwayHandler
|
||||||
onClickOutside={onClickOutside}
|
onClickOutside={onClickOutside}
|
||||||
className={`${className} border border-sky-100 py-1 shadow-md bg-gray-50 rounded-md flex flex-col z-10`}
|
className={`${className} py-1 shadow-md bg-gray-50 rounded-md flex flex-col z-10`}
|
||||||
>
|
>
|
||||||
{items.map((e, i) => {
|
{items.map((e, i) => {
|
||||||
const inner = (
|
const inner = (
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import React, { SetStateAction } from "react";
|
||||||
|
import ClickAwayHandler from "./ClickAwayHandler";
|
||||||
|
import Checkbox from "./Checkbox";
|
||||||
|
import { SearchSettings } from "@/types/global";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
setFilterDropdown: (value: SetStateAction<boolean>) => void;
|
||||||
|
toggleCheckbox: (
|
||||||
|
name: "name" | "title" | "url" | "collection" | "tags"
|
||||||
|
) => void;
|
||||||
|
searchSettings: SearchSettings;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function FilterSearchDropdown({
|
||||||
|
setFilterDropdown,
|
||||||
|
toggleCheckbox,
|
||||||
|
searchSettings,
|
||||||
|
}: Props) {
|
||||||
|
return (
|
||||||
|
<ClickAwayHandler
|
||||||
|
onClickOutside={(e: Event) => {
|
||||||
|
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 w-40"
|
||||||
|
>
|
||||||
|
<p className="mb-2 text-sky-900 text-center font-semibold">Filter by</p>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<Checkbox
|
||||||
|
label="Name"
|
||||||
|
state={searchSettings.filter.name}
|
||||||
|
onClick={() => toggleCheckbox("name")}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label="Link"
|
||||||
|
state={searchSettings.filter.url}
|
||||||
|
onClick={() => toggleCheckbox("url")}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label="Title"
|
||||||
|
state={searchSettings.filter.title}
|
||||||
|
onClick={() => toggleCheckbox("title")}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label="Collection"
|
||||||
|
state={searchSettings.filter.collection}
|
||||||
|
onClick={() => toggleCheckbox("collection")}
|
||||||
|
/>
|
||||||
|
<Checkbox
|
||||||
|
label="Tags"
|
||||||
|
state={searchSettings.filter.tags}
|
||||||
|
onClick={() => toggleCheckbox("tags")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ClickAwayHandler>
|
||||||
|
);
|
||||||
|
}
|
|
@ -78,22 +78,26 @@ export default function ({ link, count }: Props) {
|
||||||
<div className="flex justify-between gap-5 w-full h-full z-0">
|
<div className="flex justify-between gap-5 w-full h-full z-0">
|
||||||
<div className="flex flex-col justify-between">
|
<div className="flex flex-col justify-between">
|
||||||
<div className="flex items-baseline gap-1">
|
<div className="flex items-baseline gap-1">
|
||||||
<p className="text-sm text-sky-300 font-bold">{count + 1}.</p>
|
<p className="text-sm text-sky-400 font-bold">{count + 1}.</p>
|
||||||
<p className="text-lg text-sky-600 font-bold">{link.name}</p>
|
<p className="text-lg text-sky-600 font-bold">{link.name}</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sky-400 text-sm font-medium">{link.title}</p>
|
<p className="text-sky-400 text-sm font-medium">{link.title}</p>
|
||||||
<div className="flex gap-3 items-center flex-wrap my-3">
|
<div className="flex gap-3 items-center flex-wrap my-3">
|
||||||
<Link href={`/collections/${link.collection.id}`}>
|
<Link href={`/collections/${link.collection.id}`}>
|
||||||
<div className="flex items-center gap-1 cursor-pointer hover:opacity-60 duration-100">
|
<div className="flex items-center gap-1 cursor-pointer hover:opacity-60 duration-100">
|
||||||
<FontAwesomeIcon icon={faFolder} className="w-4 text-sky-300" />
|
<FontAwesomeIcon
|
||||||
|
icon={faFolder}
|
||||||
|
className="w-4 h-4 mt-1"
|
||||||
|
style={{ color: link.collection.color }}
|
||||||
|
/>
|
||||||
<p className="text-sky-900">{link.collection.name}</p>
|
<p className="text-sky-900">{link.collection.name}</p>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex gap-1 items-center flex-wrap">
|
<div className="flex gap-1 items-center flex-wrap mt-1">
|
||||||
{link.tags.map((e, i) => (
|
{link.tags.map((e, i) => (
|
||||||
<Link key={i} href={`/tags/${e.id}`}>
|
<Link key={i} href={`/tags/${e.id}`}>
|
||||||
<p className="px-2 py-1 bg-sky-200 text-sky-700 text-xs rounded-3xl cursor-pointer hover:bg-sky-100 duration-100">
|
<p className="px-2 py-1 bg-sky-200 text-sky-700 text-xs rounded-3xl cursor-pointer hover:opacity-60 duration-100">
|
||||||
{e.name}
|
{e.name}
|
||||||
</p>
|
</p>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default function ChangePassword({
|
||||||
<div className="max-w-sm mx-auto flex flex-col gap-3">
|
<div className="max-w-sm mx-auto flex flex-col gap-3">
|
||||||
<p className="text-xl text-sky-500 mb-2 text-center">Change Password</p>
|
<p className="text-xl text-sky-500 mb-2 text-center">Change Password</p>
|
||||||
|
|
||||||
<p className="text-sm font-bold text-sky-300">Old Password</p>
|
<p className="text-sm text-sky-500">Old Password</p>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
value={oldPassword}
|
value={oldPassword}
|
||||||
|
@ -42,7 +42,7 @@ export default function ChangePassword({
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||||
/>
|
/>
|
||||||
<p className="text-sm font-bold text-sky-300">New Password</p>
|
<p className="text-sm text-sky-500">New Password</p>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
value={newPassword1}
|
value={newPassword1}
|
||||||
|
@ -50,7 +50,7 @@ export default function ChangePassword({
|
||||||
type="text"
|
type="text"
|
||||||
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
className="w-full rounded-md p-3 mx-auto border-sky-100 border-solid border outline-none focus:border-sky-500 duration-100"
|
||||||
/>
|
/>
|
||||||
<p className="text-sm font-bold text-sky-300">Re-enter New Password</p>
|
<p className="text-sm text-sky-500">Re-enter New Password</p>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
value={newPassword2}
|
value={newPassword2}
|
||||||
|
@ -60,7 +60,7 @@ export default function ChangePassword({
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="mx-auto mt-2 text-white flex items-center gap-2 py-2 px-5 rounded-md select-none font-bold duration-100 bg-sky-500 hover:bg-sky-400 cursor-pointer"
|
className="mx-auto mt-2 text-white flex items-center gap-2 py-2 px-5 rounded-md select-none duration-100 bg-sky-500 hover:bg-sky-400 cursor-pointer"
|
||||||
onClick={submit}
|
onClick={submit}
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
|
|
|
@ -47,7 +47,7 @@ export default function CollectionInfo({
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row gap-3">
|
<div className="flex flex-col sm:flex-row gap-3">
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">
|
<p className="text-sm text-sky-500 mb-2">
|
||||||
Name
|
Name
|
||||||
<RequiredBadge />
|
<RequiredBadge />
|
||||||
</p>
|
</p>
|
||||||
|
@ -63,9 +63,7 @@ export default function CollectionInfo({
|
||||||
/>
|
/>
|
||||||
<div className="color-picker flex justify-between">
|
<div className="color-picker flex justify-between">
|
||||||
<div className="flex flex-col justify-between items-center w-32">
|
<div className="flex flex-col justify-between items-center w-32">
|
||||||
<p className="text-sm w-full font-bold text-sky-300 mb-2">
|
<p className="text-sm w-full text-sky-500 mb-2">Icon Color</p>
|
||||||
Icon Color
|
|
||||||
</p>
|
|
||||||
<div style={{ color: collection.color }}>
|
<div style={{ color: collection.color }}>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faFolder}
|
icon={faFolder}
|
||||||
|
@ -73,9 +71,9 @@ export default function CollectionInfo({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
className="py-1 px-2 rounded-md text-xs font-semibold cursor-pointer text-gray-500 hover:bg-slate-200 duration-100"
|
className="py-1 px-2 rounded-md text-xs font-semibold cursor-pointer text-sky-500 hover:bg-slate-200 duration-100"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
setCollection({ ...collection, color: "#7dd3fc" })
|
setCollection({ ...collection, color: "#0ea5e9" })
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
Reset
|
Reset
|
||||||
|
@ -90,7 +88,7 @@ export default function CollectionInfo({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">Description</p>
|
<p className="text-sm text-sky-500 mb-2">Description</p>
|
||||||
<textarea
|
<textarea
|
||||||
className="w-full h-[11.4rem] resize-none border rounded-md duration-100 bg-white p-3 outline-none border-sky-100 focus:border-sky-500"
|
className="w-full h-[11.4rem] resize-none border rounded-md duration-100 bg-white p-3 outline-none border-sky-100 focus:border-sky-500"
|
||||||
placeholder="The purpose of this Collection..."
|
placeholder="The purpose of this Collection..."
|
||||||
|
|
|
@ -77,7 +77,7 @@ export default function TeamManagement({
|
||||||
Sharing & Collaboration
|
Sharing & Collaboration
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="text-sm font-bold text-sky-300">Make Public</p>
|
<p className="text-sm text-sky-500">Make Public</p>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label="Make this a public collection."
|
label="Make this a public collection."
|
||||||
|
@ -93,7 +93,9 @@ export default function TeamManagement({
|
||||||
|
|
||||||
{collection.isPublic ? (
|
{collection.isPublic ? (
|
||||||
<div>
|
<div>
|
||||||
<p className="mb-2 text-gray-500">Public Link (Click to copy)</p>
|
<p className="text-sm text-sky-500 mb-2">
|
||||||
|
Public Link (Click to copy)
|
||||||
|
</p>
|
||||||
<div
|
<div
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
try {
|
try {
|
||||||
|
@ -113,7 +115,7 @@ export default function TeamManagement({
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<p className="text-sm font-bold text-sky-300">Member Management</p>
|
<p className="text-sm text-sky-500">Member Management</p>
|
||||||
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<input
|
<input
|
||||||
|
@ -202,10 +204,10 @@ export default function TeamManagement({
|
||||||
</div>
|
</div>
|
||||||
<div className="flex sm:block items-center gap-5">
|
<div className="flex sm:block items-center gap-5">
|
||||||
<div>
|
<div>
|
||||||
<p className="font-bold text-sm text-gray-500">
|
<p className="font-bold text-sm text-sky-500">
|
||||||
Permissions
|
Permissions
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-gray-400 mb-2">
|
<p className="text-xs text-gray-500 mb-2">
|
||||||
(Click to toggle.)
|
(Click to toggle.)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -115,7 +115,7 @@ export default function EditLink({
|
||||||
|
|
||||||
<div className="grid sm:grid-cols-2 gap-3">
|
<div className="grid sm:grid-cols-2 gap-3">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">
|
<p className="text-sm text-sky-500 mb-2">
|
||||||
Name
|
Name
|
||||||
<RequiredBadge />
|
<RequiredBadge />
|
||||||
</p>
|
</p>
|
||||||
|
@ -130,7 +130,7 @@ export default function EditLink({
|
||||||
|
|
||||||
{method === "CREATE" ? (
|
{method === "CREATE" ? (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">
|
<p className="text-sm text-sky-500 mb-2">
|
||||||
URL
|
URL
|
||||||
<RequiredBadge />
|
<RequiredBadge />
|
||||||
</p>
|
</p>
|
||||||
|
@ -145,7 +145,7 @@ export default function EditLink({
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">
|
<p className="text-sm text-sky-500 mb-2">
|
||||||
Collection
|
Collection
|
||||||
<RequiredBadge />
|
<RequiredBadge />
|
||||||
</p>
|
</p>
|
||||||
|
@ -167,7 +167,7 @@ export default function EditLink({
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={method === "UPDATE" ? "sm:col-span-2" : ""}>
|
<div className={method === "UPDATE" ? "sm:col-span-2" : ""}>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">Tags</p>
|
<p className="text-sm text-sky-500 mb-2">Tags</p>
|
||||||
<TagSelection
|
<TagSelection
|
||||||
onChange={setTags}
|
onChange={setTags}
|
||||||
defaultValue={link.tags.map((e) => {
|
defaultValue={link.tags.map((e) => {
|
||||||
|
|
|
@ -95,12 +95,10 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
||||||
<div className="flex flex-col gap-3 sm:w-[35rem] w-80">
|
<div className="flex flex-col gap-3 sm:w-[35rem] w-80">
|
||||||
<p className="text-xl text-sky-500 mb-2 text-center">Settings</p>
|
<p className="text-xl text-sky-500 mb-2 text-center">Settings</p>
|
||||||
|
|
||||||
<p className="text-sky-600">Profile Settings</p>
|
|
||||||
|
|
||||||
<div className="grid sm:grid-cols-2 gap-3 auto-rows-auto">
|
<div className="grid sm:grid-cols-2 gap-3 auto-rows-auto">
|
||||||
<div className="flex flex-col gap-3">
|
<div className="flex flex-col gap-3">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">Display Name</p>
|
<p className="text-sm text-sky-500 mb-2">Display Name</p>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={user.name}
|
value={user.name}
|
||||||
|
@ -110,7 +108,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">Email</p>
|
<p className="text-sm text-sky-500 mb-2">Email</p>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
value={user.email}
|
value={user.email}
|
||||||
|
@ -120,7 +118,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">Password</p>
|
<p className="text-sm text-sky-500 mb-2">Password</p>
|
||||||
|
|
||||||
<div className="w-fit" onClick={togglePasswordFormModal}>
|
<div className="w-fit" onClick={togglePasswordFormModal}>
|
||||||
<div className="border border-sky-100 rounded-md bg-white px-2 py-1 text-center select-none cursor-pointer text-sky-900 duration-100 hover:border-sky-500">
|
<div className="border border-sky-100 rounded-md bg-white px-2 py-1 text-center select-none cursor-pointer text-sky-900 duration-100 hover:border-sky-500">
|
||||||
|
@ -139,7 +137,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="sm:row-span-2 sm:justify-self-center mb-3">
|
<div className="sm:row-span-2 sm:justify-self-center mb-3">
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2 sm:text-center">
|
<p className="text-sm text-sky-500 mb-2 sm:text-center">
|
||||||
Profile Photo
|
Profile Photo
|
||||||
</p>
|
</p>
|
||||||
<div className="w-28 h-28 flex items-center justify-center border border-sky-100 rounded-full relative">
|
<div className="w-28 h-28 flex items-center justify-center border border-sky-100 rounded-full relative">
|
||||||
|
@ -202,7 +200,7 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<p className="text-sky-600">Privacy Settings</p>
|
<p className="text-sm text-sky-500 mb-2">Profile Visibility</p>
|
||||||
|
|
||||||
<Checkbox
|
<Checkbox
|
||||||
label="Make profile private"
|
label="Make profile private"
|
||||||
|
@ -211,19 +209,16 @@ export default function UserSettings({ toggleSettingsModal }: Props) {
|
||||||
onClick={() => setUser({ ...user, isPrivate: !user.isPrivate })}
|
onClick={() => setUser({ ...user, isPrivate: !user.isPrivate })}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<p className="text-gray-500 text-sm mb-3">
|
<p className="text-gray-500 text-sm">
|
||||||
This will limit who can find and add you to other Collections.
|
This will limit who can find and add you to other Collections.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{user.isPrivate ? (
|
{user.isPrivate ? (
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-bold text-sky-300 mb-2">
|
<p className="text-sm text-sky-500 mb-2">Whitelisted Users</p>
|
||||||
Whitelisted Users
|
|
||||||
</p>
|
|
||||||
<p className="text-gray-500 text-sm mb-3">
|
<p className="text-gray-500 text-sm mb-3">
|
||||||
Please provide the Email addresses of the users you wish to grant
|
Please provide the Email addresses of the users you wish to grant
|
||||||
visibility to your profile. Separate by comma. Users not included
|
visibility to your profile. Separated by comma.
|
||||||
will be unable to view your profile.
|
|
||||||
</p>
|
</p>
|
||||||
<textarea
|
<textarea
|
||||||
className="w-full resize-none border rounded-md duration-100 bg-white p-2 outline-none border-sky-100 focus:border-sky-500"
|
className="w-full resize-none border rounded-md duration-100 bg-white p-2 outline-none border-sky-100 focus:border-sky-500"
|
||||||
|
|
|
@ -51,7 +51,7 @@ export default function ({ link, count }: Props) {
|
||||||
<div className="flex justify-between items-center gap-5 w-full h-full z-0">
|
<div className="flex justify-between items-center gap-5 w-full h-full z-0">
|
||||||
<div className="flex flex-col justify-between">
|
<div className="flex flex-col justify-between">
|
||||||
<div className="flex items-baseline gap-1">
|
<div className="flex items-baseline gap-1">
|
||||||
<p className="text-sm text-sky-300 font-bold">{count + 1}.</p>
|
<p className="text-sm text-sky-400 font-bold">{count + 1}.</p>
|
||||||
<p className="text-lg text-sky-600 font-bold">{link.name}</p>
|
<p className="text-lg text-sky-600 font-bold">{link.name}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
faBookmark,
|
faBookmark,
|
||||||
faChartSimple,
|
faChartSimple,
|
||||||
} from "@fortawesome/free-solid-svg-icons";
|
} from "@fortawesome/free-solid-svg-icons";
|
||||||
import SidebarItem from "./SidebarItem";
|
|
||||||
import useTagStore from "@/store/tags";
|
import useTagStore from "@/store/tags";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
@ -23,13 +22,13 @@ export default function ({ className }: { className?: string }) {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setActive(router.asPath);
|
setActive(router.asPath);
|
||||||
}, [router]);
|
}, [router, collections]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`bg-gray-100 h-screen w-64 xl:w-80 overflow-y-auto border-solid border-r-sky-100 px-2 border z-20 ${className}`}
|
className={`bg-gray-100 h-screen w-64 xl:w-80 overflow-y-auto border-solid border-r-sky-100 px-2 border z-20 ${className}`}
|
||||||
>
|
>
|
||||||
<p className="p-3 text-sky-500 font-bold text-2xl my-2 leading-4">
|
<p className="p-2 text-sky-500 font-bold text-2xl my-2 leading-4">
|
||||||
Linkwarden
|
Linkwarden
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -38,45 +37,29 @@ export default function ({ className }: { className?: string }) {
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
active === "/dashboard"
|
active === "/dashboard"
|
||||||
? "bg-sky-500"
|
? "bg-sky-200"
|
||||||
: "hover:bg-slate-200 bg-gray-100"
|
: "hover:bg-slate-200 bg-gray-100"
|
||||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faChartSimple}
|
icon={faChartSimple}
|
||||||
className={`w-4 h-4 ${
|
className={`w-6 h-6 drop-shadow text-sky-500`}
|
||||||
active === "/dashboard" ? "text-white" : "text-sky-300"
|
|
||||||
}`}
|
|
||||||
/>
|
/>
|
||||||
<p
|
<p className="text-sky-600">Dashboard</p>
|
||||||
className={`${
|
|
||||||
active === "/dashboard" ? "text-white" : "text-sky-900"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
Dashboard
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<Link href="/collections">
|
<Link href="/collections">
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
active === "/collections" ? "bg-sky-500" : "hover:bg-slate-200"
|
active === "/collections" ? "bg-sky-200" : "hover:bg-slate-200"
|
||||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faBox}
|
icon={faBox}
|
||||||
className={`w-4 h-4 ${
|
className={`w-6 h-6 drop-shadow text-sky-500`}
|
||||||
active === "/collections" ? "text-white" : "text-sky-300"
|
|
||||||
}`}
|
|
||||||
/>
|
/>
|
||||||
<p
|
<p className="text-sky-600">All Collections</p>
|
||||||
className={`${
|
|
||||||
active === "/collections" ? "text-white" : "text-sky-900"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
All Collections
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
|
@ -84,60 +67,71 @@ export default function ({ className }: { className?: string }) {
|
||||||
<div
|
<div
|
||||||
className={`${
|
className={`${
|
||||||
active === "/links"
|
active === "/links"
|
||||||
? "bg-sky-500"
|
? "bg-sky-200"
|
||||||
: "hover:bg-slate-200 bg-gray-100"
|
: "hover:bg-slate-200 bg-gray-100"
|
||||||
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
} outline-sky-100 outline-1 duration-100 py-1 px-2 rounded-md cursor-pointer flex items-center gap-2`}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faBookmark}
|
icon={faBookmark}
|
||||||
className={`w-4 h-4 ${
|
className={`w-6 h-6 drop-shadow text-sky-500`}
|
||||||
active === "/links" ? "text-white" : "text-sky-300"
|
|
||||||
}`}
|
|
||||||
/>
|
/>
|
||||||
<p
|
<p className="text-sky-600">All Links</p>
|
||||||
className={`${
|
|
||||||
active === "/links" ? "text-white" : "text-sky-900"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
All Links
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-gray-500 mt-5">
|
<div className="text-gray-500 mt-5">
|
||||||
<p className="text-sm mb-2 pl-3 font-semibold">Collections</p>
|
<p className="text-sm mb-2 pl-2 font-semibold">Collections</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{collections
|
{collections
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
.map((e, i) => {
|
.map((e, i) => {
|
||||||
return (
|
return (
|
||||||
<SidebarItem
|
<Link key={i} href={`/collections/${e.id}`}>
|
||||||
key={i}
|
<div
|
||||||
text={e.name}
|
className={`${
|
||||||
icon={<FontAwesomeIcon icon={faFolder} />}
|
active === `/collections/${e.id}`
|
||||||
iconColor={e.color}
|
? "bg-sky-200"
|
||||||
path={`/collections/${e.id}`}
|
: "hover:bg-slate-200 bg-gray-100"
|
||||||
className="capitalize"
|
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faFolder}
|
||||||
|
className="w-6 h-6 drop-shadow"
|
||||||
|
style={{ color: e.color }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<p className="text-sky-600 truncate w-4/6">{e.name}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-gray-500 mt-5">
|
<div className="text-gray-500 mt-5">
|
||||||
<p className="text-sm mb-2 pl-3 font-semibold">Tags</p>
|
<p className="text-sm mb-2 pl-2 font-semibold">Tags</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-1">
|
<div className="flex flex-col gap-1">
|
||||||
{tags
|
{tags
|
||||||
.sort((a, b) => a.name.localeCompare(b.name))
|
.sort((a, b) => a.name.localeCompare(b.name))
|
||||||
.map((e, i) => {
|
.map((e, i) => {
|
||||||
return (
|
return (
|
||||||
<SidebarItem
|
<Link key={i} href={`/tags/${e.id}`}>
|
||||||
key={i}
|
<div
|
||||||
text={e.name}
|
className={`${
|
||||||
icon={<FontAwesomeIcon icon={faHashtag} />}
|
active === `/tags/${e.id}`
|
||||||
path={`/tags/${e.id}`}
|
? "bg-sky-200"
|
||||||
|
: "hover:bg-slate-200 bg-gray-100"
|
||||||
|
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faHashtag}
|
||||||
|
className="w-4 h-4 text-sky-500 mt-1"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<p className="text-sky-600 truncate w-4/6">{e.name}</p>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
|
@ -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 (
|
|
||||||
<Link href={path}>
|
|
||||||
<div
|
|
||||||
className={`${
|
|
||||||
active ? "bg-sky-500" : "hover:bg-slate-200 bg-gray-100"
|
|
||||||
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md ${className}`}
|
|
||||||
>
|
|
||||||
{React.cloneElement(icon, {
|
|
||||||
className: "w-4 h-4",
|
|
||||||
style: {
|
|
||||||
color: active ? "white" : iconColor ? iconColor : "#7dd3fc",
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
<p
|
|
||||||
className={`${active ? "text-white" : "text-sky-900"} truncate w-4/6`}
|
|
||||||
>
|
|
||||||
{text}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -19,7 +19,7 @@ export default function SortLinkDropdown({
|
||||||
const target = e.target as HTMLInputElement;
|
const target = e.target as HTMLInputElement;
|
||||||
if (target.id !== "sort-dropdown") toggleSortDropdown();
|
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"
|
||||||
>
|
>
|
||||||
<p className="mb-2 text-sky-900 text-center font-semibold">Sort by</p>
|
<p className="mb-2 text-sky-900 text-center font-semibold">Sort by</p>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
|
|
|
@ -4,6 +4,7 @@ export default async function (userId: number) {
|
||||||
// remove empty tags
|
// remove empty tags
|
||||||
await prisma.tag.deleteMany({
|
await prisma.tag.deleteMany({
|
||||||
where: {
|
where: {
|
||||||
|
ownerId: userId,
|
||||||
links: {
|
links: {
|
||||||
none: {},
|
none: {},
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,3 +9,10 @@ export const prisma =
|
||||||
});
|
});
|
||||||
|
|
||||||
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = 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
|
||||||
|
});
|
||||||
|
|
|
@ -46,7 +46,6 @@ export default async function (req: NextApiRequest, res: NextApiResponse) {
|
||||||
`data/uploads/avatar/${queryId}.jpg`
|
`data/uploads/avatar/${queryId}.jpg`
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log(filePath);
|
|
||||||
const file = fs.existsSync(filePath)
|
const file = fs.existsSync(filePath)
|
||||||
? fs.readFileSync(filePath)
|
? fs.readFileSync(filePath)
|
||||||
: "File not found.";
|
: "File not found.";
|
||||||
|
|
|
@ -85,7 +85,7 @@ export default function () {
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faBox}
|
icon={faBox}
|
||||||
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-300"
|
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-500"
|
||||||
/>
|
/>
|
||||||
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
||||||
All Collections
|
All Collections
|
||||||
|
@ -218,7 +218,7 @@ export default function () {
|
||||||
activeCollection={{
|
activeCollection={{
|
||||||
name: "",
|
name: "",
|
||||||
description: "",
|
description: "",
|
||||||
color: "#7dd3fc",
|
color: "#0ea5e9",
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
ownerId: session.data?.user.id as number,
|
ownerId: session.data?.user.id as number,
|
||||||
members: [],
|
members: [],
|
||||||
|
|
|
@ -39,7 +39,7 @@ export default function () {
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faChartSimple}
|
icon={faChartSimple}
|
||||||
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-300"
|
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-500"
|
||||||
/>
|
/>
|
||||||
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
||||||
Dashboard
|
Dashboard
|
||||||
|
|
|
@ -55,7 +55,7 @@ export default function Links() {
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faBookmark}
|
icon={faBookmark}
|
||||||
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-300"
|
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-500"
|
||||||
/>
|
/>
|
||||||
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
||||||
All Links
|
All Links
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
import Checkbox from "@/components/Checkbox";
|
import FilterSearchDropdown from "@/components/FilterSearchDropdown";
|
||||||
import ClickAwayHandler from "@/components/ClickAwayHandler";
|
|
||||||
import LinkList from "@/components/LinkList";
|
import LinkList from "@/components/LinkList";
|
||||||
import RadioButton from "@/components/RadioButton";
|
|
||||||
import SortLinkDropdown from "@/components/SortLinkDropdown";
|
import SortLinkDropdown from "@/components/SortLinkDropdown";
|
||||||
import MainLayout from "@/layouts/MainLayout";
|
import MainLayout from "@/layouts/MainLayout";
|
||||||
import useLinkStore from "@/store/links";
|
import useLinkStore from "@/store/links";
|
||||||
|
@ -17,8 +15,7 @@ export default function Links() {
|
||||||
const [sortDropdown, setSortDropdown] = useState(false);
|
const [sortDropdown, setSortDropdown] = useState(false);
|
||||||
const [sortBy, setSortBy] = useState("Name (A-Z)");
|
const [sortBy, setSortBy] = useState("Name (A-Z)");
|
||||||
const [sortedLinks, setSortedLinks] = useState(links);
|
const [sortedLinks, setSortedLinks] = useState(links);
|
||||||
const { searchSettings, toggleCheckbox, setSearchSettings, setSearchQuery } =
|
const { searchSettings, toggleCheckbox } = useSearchSettingsStore();
|
||||||
useSearchSettingsStore();
|
|
||||||
|
|
||||||
const handleSortChange = (event: ChangeEvent<HTMLInputElement>) => {
|
const handleSortChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||||
setSortBy(event.target.value);
|
setSortBy(event.target.value);
|
||||||
|
@ -93,45 +90,11 @@ export default function Links() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{filterDropdown ? (
|
{filterDropdown ? (
|
||||||
<ClickAwayHandler
|
<FilterSearchDropdown
|
||||||
onClickOutside={(e: Event) => {
|
setFilterDropdown={setFilterDropdown}
|
||||||
const target = e.target as HTMLInputElement;
|
searchSettings={searchSettings}
|
||||||
if (target.id !== "filter-dropdown")
|
toggleCheckbox={toggleCheckbox}
|
||||||
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"
|
|
||||||
>
|
|
||||||
<p className="mb-2 text-sky-900 text-center font-semibold">
|
|
||||||
Filter by
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col gap-2">
|
|
||||||
<Checkbox
|
|
||||||
label="Name"
|
|
||||||
state={searchSettings.filter.name}
|
|
||||||
onClick={() => toggleCheckbox("name")}
|
|
||||||
/>
|
/>
|
||||||
<Checkbox
|
|
||||||
label="Link"
|
|
||||||
state={searchSettings.filter.url}
|
|
||||||
onClick={() => toggleCheckbox("url")}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label="Title"
|
|
||||||
state={searchSettings.filter.title}
|
|
||||||
onClick={() => toggleCheckbox("title")}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label="Collection"
|
|
||||||
state={searchSettings.filter.collection}
|
|
||||||
onClick={() => toggleCheckbox("collection")}
|
|
||||||
/>
|
|
||||||
<Checkbox
|
|
||||||
label="Tags"
|
|
||||||
state={searchSettings.filter.tags}
|
|
||||||
onClick={() => toggleCheckbox("tags")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ClickAwayHandler>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -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<HTMLInputElement>) => {
|
||||||
|
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 (
|
||||||
|
<MainLayout>
|
||||||
|
<div className="p-5 flex flex-col gap-5 w-full">
|
||||||
|
<div className="flex gap-3 items-center justify-between">
|
||||||
|
<div className="flex gap-3 items-center mb-5">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faSearch}
|
||||||
|
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-500"
|
||||||
|
/>
|
||||||
|
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
||||||
|
Search Results
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex gap-3 items-center">
|
||||||
|
<div className="relative">
|
||||||
|
<div
|
||||||
|
onClick={() => setFilterDropdown(!filterDropdown)}
|
||||||
|
id="filter-dropdown"
|
||||||
|
className="inline-flex rounded-md cursor-pointer hover:bg-slate-200 duration-100 p-1"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faFilter}
|
||||||
|
id="filter-dropdown"
|
||||||
|
className="w-5 h-5 text-gray-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{filterDropdown ? (
|
||||||
|
<FilterSearchDropdown
|
||||||
|
setFilterDropdown={setFilterDropdown}
|
||||||
|
searchSettings={searchSettings}
|
||||||
|
toggleCheckbox={toggleCheckbox}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative">
|
||||||
|
<div
|
||||||
|
onClick={() => setSortDropdown(!sortDropdown)}
|
||||||
|
id="sort-dropdown"
|
||||||
|
className="inline-flex rounded-md cursor-pointer hover:bg-slate-200 duration-100 p-1"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon={faSort}
|
||||||
|
id="sort-dropdown"
|
||||||
|
className="w-5 h-5 text-gray-500"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{sortDropdown ? (
|
||||||
|
<SortLinkDropdown
|
||||||
|
handleSortChange={handleSortChange}
|
||||||
|
sortBy={sortBy}
|
||||||
|
toggleSortDropdown={() => setSortDropdown(!sortDropdown)}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{sortedLinks[0] ? (
|
||||||
|
sortedLinks.map((e, i) => {
|
||||||
|
return <LinkList key={i} link={e} count={i} />;
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<p className="text-sky-900">
|
||||||
|
Nothing found.{" "}
|
||||||
|
<span className="text-sky-500 font-bold text-xl" title="Shruggie">
|
||||||
|
¯\_(ツ)_/¯
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</MainLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -71,7 +71,7 @@ export default function () {
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<FontAwesomeIcon
|
<FontAwesomeIcon
|
||||||
icon={faHashtag}
|
icon={faHashtag}
|
||||||
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-300"
|
className="sm:w-8 sm:h-8 w-6 h-6 mt-2 text-sky-500"
|
||||||
/>
|
/>
|
||||||
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
<p className="sm:text-4xl text-3xl capitalize bg-gradient-to-tr from-sky-500 to-slate-400 bg-clip-text text-transparent font-bold">
|
||||||
{activeTag?.name}
|
{activeTag?.name}
|
||||||
|
|
|
@ -16,7 +16,7 @@ CREATE TABLE "Collection" (
|
||||||
"id" SERIAL NOT NULL,
|
"id" SERIAL NOT NULL,
|
||||||
"name" TEXT NOT NULL,
|
"name" TEXT NOT NULL,
|
||||||
"description" TEXT NOT NULL DEFAULT '',
|
"description" TEXT NOT NULL DEFAULT '',
|
||||||
"color" TEXT NOT NULL DEFAULT '#7dd3fc',
|
"color" TEXT NOT NULL DEFAULT '#0ea5e9',
|
||||||
"isPublic" BOOLEAN NOT NULL DEFAULT false,
|
"isPublic" BOOLEAN NOT NULL DEFAULT false,
|
||||||
"ownerId" INTEGER NOT NULL,
|
"ownerId" INTEGER NOT NULL,
|
||||||
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
@ -24,7 +24,7 @@ model Collection {
|
||||||
id Int @id @default(autoincrement())
|
id Int @id @default(autoincrement())
|
||||||
name String
|
name String
|
||||||
description String @default("")
|
description String @default("")
|
||||||
color String @default("#7dd3fc")
|
color String @default("#0ea5e9")
|
||||||
isPublic Boolean @default(false)
|
isPublic Boolean @default(false)
|
||||||
owner User @relation(fields: [ownerId], references: [id])
|
owner User @relation(fields: [ownerId], references: [id])
|
||||||
ownerId Int
|
ownerId Int
|
||||||
|
|
Ŝarĝante…
Reference in New Issue