many bug fixes + add links and collections together + more changes

This commit is contained in:
daniel31x13 2023-12-01 16:29:17 -05:00
parent a3c6d9b42e
commit a36769c521
27 changed files with 169 additions and 190 deletions

View File

@ -5,7 +5,6 @@ import { CollectionIncludingMembersAndLinkCount } from "@/types/global";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import ProfilePhoto from "./ProfilePhoto"; import ProfilePhoto from "./ProfilePhoto";
import { faCalendarDays } from "@fortawesome/free-regular-svg-icons"; import { faCalendarDays } from "@fortawesome/free-regular-svg-icons";
import useModalStore from "@/store/modals";
import usePermissions from "@/hooks/usePermissions"; import usePermissions from "@/hooks/usePermissions";
import useLocalSettingsStore from "@/store/localSettings"; import useLocalSettingsStore from "@/store/localSettings";
import getPublicUserData from "@/lib/client/getPublicUserData"; import getPublicUserData from "@/lib/client/getPublicUserData";
@ -20,7 +19,6 @@ type Props = {
}; };
export default function CollectionCard({ collection, className }: Props) { export default function CollectionCard({ collection, className }: Props) {
const { setModal } = useModalStore();
const { settings } = useLocalSettingsStore(); const { settings } = useLocalSettingsStore();
const { account } = useAccountStore(); const { account } = useAccountStore();

View File

@ -10,7 +10,7 @@ type Props = {
export default function dashboardItem({ name, value, icon }: Props) { export default function dashboardItem({ name, value, icon }: Props) {
return ( return (
<div className="flex gap-4 items-end"> <div className="flex gap-4 items-end">
<div className="p-4 bg-secondary/30 rounded-xl select-none"> <div className="p-4 bg-primary/20 rounded-xl select-none">
<FontAwesomeIcon icon={icon} className="w-8 h-8 text-primary" /> <FontAwesomeIcon icon={icon} className="w-8 h-8 text-primary" />
</div> </div>
<div className="flex flex-col justify-center"> <div className="flex flex-col justify-center">

View File

@ -1,9 +1,9 @@
import useCollectionStore from "@/store/collections"; import useCollectionStore from "@/store/collections";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Select from "react-select";
import { styles } from "./styles"; import { styles } from "./styles";
import { Options } from "./types"; import { Options } from "./types";
import CreatableSelect from "react-select/creatable";
type Props = { type Props = {
onChange: any; onChange: any;
@ -13,9 +13,14 @@ type Props = {
value?: number; value?: number;
} }
| undefined; | undefined;
id?: string;
}; };
export default function CollectionSelection({ onChange, defaultValue }: Props) { export default function CollectionSelection({
onChange,
defaultValue,
id,
}: Props) {
const { collections } = useCollectionStore(); const { collections } = useCollectionStore();
const router = useRouter(); const router = useRouter();
@ -43,7 +48,8 @@ export default function CollectionSelection({ onChange, defaultValue }: Props) {
}, [collections]); }, [collections]);
return ( return (
<Select <CreatableSelect
key={id || "key"}
isClearable={false} isClearable={false}
className="react-select-container" className="react-select-container"
classNamePrefix="react-select" classNamePrefix="react-select"

View File

@ -10,11 +10,9 @@ import {
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import Dropdown from "./Dropdown";
import useLinkStore from "@/store/links"; import useLinkStore from "@/store/links";
import useCollectionStore from "@/store/collections"; import useCollectionStore from "@/store/collections";
import useAccountStore from "@/store/account"; import useAccountStore from "@/store/account";
import useModalStore from "@/store/modals";
import { faCalendarDays } from "@fortawesome/free-regular-svg-icons"; import { faCalendarDays } from "@fortawesome/free-regular-svg-icons";
import usePermissions from "@/hooks/usePermissions"; import usePermissions from "@/hooks/usePermissions";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
@ -30,22 +28,11 @@ type Props = {
className?: string; className?: string;
}; };
type DropdownTrigger =
| {
x: number;
y: number;
}
| false;
export default function LinkCard({ link, count, className }: Props) { export default function LinkCard({ link, count, className }: Props) {
const { setModal } = useModalStore();
const router = useRouter(); const router = useRouter();
const permissions = usePermissions(link.collection.id as number); const permissions = usePermissions(link.collection.id as number);
const [expandDropdown, setExpandDropdown] = useState<DropdownTrigger>(false);
const { collections } = useCollectionStore(); const { collections } = useCollectionStore();
const { links } = useLinkStore(); const { links } = useLinkStore();
@ -82,8 +69,6 @@ export default function LinkCard({ link, count, className }: Props) {
const load = toast.loading("Applying..."); const load = toast.loading("Applying...");
setExpandDropdown(false);
const response = await updateLink({ const response = await updateLink({
...link, ...link,
pinnedBy: isAlreadyPinned ? undefined : [{ id: account.id }], pinnedBy: isAlreadyPinned ? undefined : [{ id: account.id }],
@ -98,8 +83,6 @@ export default function LinkCard({ link, count, className }: Props) {
const updateArchive = async () => { const updateArchive = async () => {
const load = toast.loading("Sending request..."); const load = toast.loading("Sending request...");
setExpandDropdown(false);
const response = await fetch(`/api/v1/links/${link.id}/archive`, { const response = await fetch(`/api/v1/links/${link.id}/archive`, {
method: "PUT", method: "PUT",
}); });
@ -122,7 +105,6 @@ export default function LinkCard({ link, count, className }: Props) {
toast.dismiss(load); toast.dismiss(load);
response.ok && toast.success(`Link Deleted.`); response.ok && toast.success(`Link Deleted.`);
setExpandDropdown(false);
}; };
const url = isValidUrl(link.url) ? new URL(link.url) : undefined; const url = isValidUrl(link.url) ? new URL(link.url) : undefined;

View File

@ -93,7 +93,7 @@ export default function PreservedFormats() {
{link?.screenshotPath && link?.screenshotPath !== "pending" ? ( {link?.screenshotPath && link?.screenshotPath !== "pending" ? (
<div className="flex justify-between items-center pr-1 border border-neutral-content rounded-md"> <div className="flex justify-between items-center pr-1 border border-neutral-content rounded-md">
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<div className="text-white bg-secondary p-2 rounded-l-md"> <div className="text-white bg-primary p-2 rounded-l-md">
<FontAwesomeIcon icon={faFileImage} className="w-6 h-6" /> <FontAwesomeIcon icon={faFileImage} className="w-6 h-6" />
</div> </div>
@ -128,7 +128,7 @@ export default function PreservedFormats() {
{link?.pdfPath && link.pdfPath !== "pending" ? ( {link?.pdfPath && link.pdfPath !== "pending" ? (
<div className="flex justify-between items-center pr-1 border border-neutral-content rounded-md"> <div className="flex justify-between items-center pr-1 border border-neutral-content rounded-md">
<div className="flex gap-2 items-center"> <div className="flex gap-2 items-center">
<div className="text-white bg-secondary p-2 rounded-l-md"> <div className="text-white bg-primary p-2 rounded-l-md">
<FontAwesomeIcon icon={faFilePdf} className="w-6 h-6" /> <FontAwesomeIcon icon={faFilePdf} className="w-6 h-6" />
</div> </div>

View File

@ -28,7 +28,15 @@ export default function DeleteCollectionModal({
}: Props) { }: Props) {
const modal = document.getElementById(modalId); const modal = document.getElementById(modalId);
const [collection, setCollection] =
useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
useEffect(() => { useEffect(() => {
modal?.scrollTo(0, 0);
setCollection(activeCollection);
setInputField("");
modal?.addEventListener("close", () => { modal?.addEventListener("close", () => {
onClose(); onClose();
}); });
@ -40,14 +48,6 @@ export default function DeleteCollectionModal({
}; };
}, [isOpen]); }, [isOpen]);
const [collection, setCollection] =
useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
useEffect(() => {
setCollection(activeCollection);
setInputField("");
}, [isOpen]);
const [submitLoader, setSubmitLoader] = useState(false); const [submitLoader, setSubmitLoader] = useState(false);
const { removeCollection } = useCollectionStore(); const { removeCollection } = useCollectionStore();
const router = useRouter(); const router = useRouter();
@ -85,7 +85,7 @@ export default function DeleteCollectionModal({
return ( return (
<dialog <dialog
id={modalId} id={modalId}
className="modal backdrop-blur-sm overflow-y-auto" className="modal backdrop-blur-sm overflow-y-auto p-5"
open={isOpen} open={isOpen}
> >
<Toaster <Toaster

View File

@ -23,6 +23,9 @@ export default function EditCollectionModal({
const modal = document.getElementById(modalId); const modal = document.getElementById(modalId);
useEffect(() => { useEffect(() => {
modal?.scrollTo(0, 0);
setCollection(activeCollection);
modal?.addEventListener("close", () => { modal?.addEventListener("close", () => {
onClose(); onClose();
}); });
@ -37,10 +40,6 @@ export default function EditCollectionModal({
const [collection, setCollection] = const [collection, setCollection] =
useState<CollectionIncludingMembersAndLinkCount>(activeCollection); useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
useEffect(() => {
setCollection(activeCollection);
}, [isOpen]);
const [submitLoader, setSubmitLoader] = useState(false); const [submitLoader, setSubmitLoader] = useState(false);
const { updateCollection } = useCollectionStore(); const { updateCollection } = useCollectionStore();
@ -71,7 +70,7 @@ export default function EditCollectionModal({
return ( return (
<dialog <dialog
id={modalId} id={modalId}
className="modal backdrop-blur-sm overflow-y-auto" className="modal backdrop-blur-sm overflow-y-auto p-5"
open={isOpen} open={isOpen}
> >
<Toaster <Toaster

View File

@ -31,6 +31,16 @@ export default function EditCollectionSharingModal({
const modal = document.getElementById(modalId); const modal = document.getElementById(modalId);
useEffect(() => { useEffect(() => {
const fetchOwner = async () => {
const owner = await getPublicUserData(collection.ownerId as number);
setCollectionOwner(owner);
};
fetchOwner();
modal?.scrollTo(0, 0);
setCollection(activeCollection);
modal?.addEventListener("close", () => { modal?.addEventListener("close", () => {
onClose(); onClose();
}); });
@ -45,10 +55,6 @@ export default function EditCollectionSharingModal({
const [collection, setCollection] = const [collection, setCollection] =
useState<CollectionIncludingMembersAndLinkCount>(activeCollection); useState<CollectionIncludingMembersAndLinkCount>(activeCollection);
useEffect(() => {
setCollection(activeCollection);
}, [isOpen]);
const [submitLoader, setSubmitLoader] = useState(false); const [submitLoader, setSubmitLoader] = useState(false);
const { updateCollection } = useCollectionStore(); const { updateCollection } = useCollectionStore();
@ -92,15 +98,6 @@ export default function EditCollectionSharingModal({
image: "", image: "",
}); });
useEffect(() => {
const fetchOwner = async () => {
const owner = await getPublicUserData(collection.ownerId as number);
setCollectionOwner(owner);
};
fetchOwner();
}, []);
const setMemberState = (newMember: Member) => { const setMemberState = (newMember: Member) => {
if (!collection) return null; if (!collection) return null;
@ -115,7 +112,7 @@ export default function EditCollectionSharingModal({
return ( return (
<dialog <dialog
id={modalId} id={modalId}
className="modal backdrop-blur-sm overflow-y-auto" className="modal backdrop-blur-sm overflow-y-auto p-5"
open={isOpen} open={isOpen}
> >
<Toaster <Toaster

View File

@ -58,6 +58,8 @@ export default function EditLinkModal({
}; };
useEffect(() => { useEffect(() => {
modal?.scrollTo(0, 0);
setLink(activeLink); setLink(activeLink);
modal?.addEventListener("close", () => { modal?.addEventListener("close", () => {
@ -97,7 +99,7 @@ export default function EditLinkModal({
return ( return (
<dialog <dialog
id={modalId} id={modalId}
className="modal backdrop-blur-sm overflow-y-auto" className="modal backdrop-blur-sm overflow-y-auto p-5"
open={isOpen} open={isOpen}
> >
<Toaster <Toaster

View File

@ -20,18 +20,6 @@ export default function NewCollectionModal({
}: Props) { }: Props) {
const modal = document.getElementById(modalId); const modal = document.getElementById(modalId);
useEffect(() => {
modal?.addEventListener("close", () => {
onClose();
});
return () => {
modal?.addEventListener("close", () => {
onClose();
});
};
}, [isOpen]);
const initial = { const initial = {
name: "", name: "",
description: "", description: "",
@ -41,7 +29,19 @@ export default function NewCollectionModal({
const [collection, setCollection] = useState<Partial<Collection>>(initial); const [collection, setCollection] = useState<Partial<Collection>>(initial);
useEffect(() => { useEffect(() => {
modal?.scrollTo(0, 0);
modal?.addEventListener("close", () => {
onClose();
});
setCollection(initial); setCollection(initial);
return () => {
modal?.addEventListener("close", () => {
onClose();
});
};
}, [isOpen]); }, [isOpen]);
const [submitLoader, setSubmitLoader] = useState(false); const [submitLoader, setSubmitLoader] = useState(false);
@ -74,7 +74,7 @@ export default function NewCollectionModal({
return ( return (
<dialog <dialog
id={modalId} id={modalId}
className="modal backdrop-blur-sm overflow-y-auto" className="modal backdrop-blur-sm overflow-y-auto p-5"
open={isOpen} open={isOpen}
> >
<Toaster <Toaster

View File

@ -43,6 +43,8 @@ export default function NewLinkModal({ modalId, isOpen, onClose }: Props) {
const { addLink } = useLinkStore(); const { addLink } = useLinkStore();
const [submitLoader, setSubmitLoader] = useState(false); const [submitLoader, setSubmitLoader] = useState(false);
const [resetCollectionSelection, setResetCollectionSelection] = useState("");
const [optionsExpanded, setOptionsExpanded] = useState(false); const [optionsExpanded, setOptionsExpanded] = useState(false);
const router = useRouter(); const router = useRouter();
@ -66,6 +68,10 @@ export default function NewLinkModal({ modalId, isOpen, onClose }: Props) {
}; };
useEffect(() => { useEffect(() => {
setResetCollectionSelection(Date.now().toString());
console.log(link);
modal?.scrollTo(0, 0);
setOptionsExpanded(false); setOptionsExpanded(false);
if (router.query.id) { if (router.query.id) {
const currentCollection = collections.find( const currentCollection = collections.find(
@ -89,7 +95,6 @@ export default function NewLinkModal({ modalId, isOpen, onClose }: Props) {
setLink({ setLink({
...initial, ...initial,
collection: { collection: {
// id: ,
name: "Unorganized", name: "Unorganized",
ownerId: data?.user.id as number, ownerId: data?.user.id as number,
}, },
@ -120,7 +125,7 @@ export default function NewLinkModal({ modalId, isOpen, onClose }: Props) {
if (response.ok) { if (response.ok) {
toast.success(`Created!`); toast.success(`Created!`);
(document.getElementById(modalId) as any).close(); (document?.getElementById(modalId) as any)?.close();
} else toast.error(response.data as string); } else toast.error(response.data as string);
setSubmitLoader(false); setSubmitLoader(false);
@ -132,7 +137,7 @@ export default function NewLinkModal({ modalId, isOpen, onClose }: Props) {
return ( return (
<dialog <dialog
id={modalId} id={modalId}
className="modal backdrop-blur-sm overflow-y-auto" className="modal backdrop-blur-sm overflow-y-auto p-5"
open={isOpen} open={isOpen}
> >
<Toaster <Toaster
@ -166,21 +171,11 @@ export default function NewLinkModal({ modalId, isOpen, onClose }: Props) {
{link.collection.name ? ( {link.collection.name ? (
<CollectionSelection <CollectionSelection
onChange={setCollection} onChange={setCollection}
// defaultValue={{ defaultValue={{
// label: link.collection.name,
// value: link.collection.id,
// }}
defaultValue={
link.collection.id
? {
value: link.collection.id,
label: link.collection.name, label: link.collection.name,
} value: link.collection.id,
: { }}
value: null as unknown as number, id={resetCollectionSelection}
label: "Unorganized",
}
}
/> />
) : null} ) : null}
</div> </div>

View File

@ -165,12 +165,12 @@ export default function Navbar() {
<NewLinkModal <NewLinkModal
isOpen={newLinkModal} isOpen={newLinkModal}
onClose={() => setNewLinkModal(false)} onClose={() => setNewLinkModal(false)}
modalId="new-link-modal" modalId="new-link-modal-nav"
/> />
<NewCollectionModal <NewCollectionModal
isOpen={newCollectionModal} isOpen={newCollectionModal}
onClose={() => setNewCollectionModal(false)} onClose={() => setNewCollectionModal(false)}
modalId="new-collection-modal" modalId="new-collection-modal-nav"
/> />
</div> </div>
); );

View File

@ -1,14 +1,14 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import React from "react"; import React, { useState } from "react";
import useModalStore from "@/store/modals"; import NewLinkModal from "./Modals/NewLinkModal";
type Props = { type Props = {
text?: string; text?: string;
}; };
export default function NoLinksFound({ text }: Props) { export default function NoLinksFound({ text }: Props) {
const { setModal } = useModalStore(); const [newLinkModal, setNewLinkModal] = useState(false);
return ( return (
<div className="border border-solid border-neutral-content w-full h-full flex flex-col justify-center p-10 rounded-2xl bg-base-200"> <div className="border border-solid border-neutral-content w-full h-full flex flex-col justify-center p-10 rounded-2xl bg-base-200">
@ -18,23 +18,24 @@ export default function NoLinksFound({ text }: Props) {
<div className="text-center w-full mt-4"> <div className="text-center w-full mt-4">
<div <div
onClick={() => { onClick={() => {
setModal({ setNewLinkModal(true);
modal: "LINK",
state: true,
method: "CREATE",
});
}} }}
className="inline-flex gap-1 relative w-[11.4rem] items-center font-semibold select-none cursor-pointer p-2 px-3 rounded-full dark:hover:bg-sky-600 text-white bg-sky-700 hover:bg-sky-600 duration-100 group" className="inline-flex gap-1 relative w-[11rem] items-center btn btn-accent text-white group"
> >
<FontAwesomeIcon <FontAwesomeIcon
icon={faPlus} icon={faPlus}
className="w-5 h-5 group-hover:ml-[4.325rem] absolute duration-100" className="w-5 h-5 left-4 group-hover:ml-[4rem] absolute duration-100"
/> />
<span className="group-hover:opacity-0 text-right w-full duration-100"> <span className="group-hover:opacity-0 text-right w-full duration-100">
Create New Link Create New Link
</span> </span>
</div> </div>
</div> </div>
<NewLinkModal
isOpen={newLinkModal}
onClose={() => setNewLinkModal(false)}
modalId="new-link-modal"
/>
</div> </div>
); );
} }

View File

@ -57,7 +57,7 @@ export default function LinkCard({ link, count }: Props) {
<Link <Link
href={"/public/collections/20?q=" + e.name} href={"/public/collections/20?q=" + e.name}
key={i} key={i}
className="px-2 bg-secondary text-white text-xs rounded-md cursor-pointer hover:opacity-60 duration-100 truncate max-w-[19rem]" className="btn btn-xs btn-outline truncate max-w-[19rem]"
> >
{e.name} {e.name}
</Link> </Link>

View File

@ -1,6 +1,6 @@
import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons"; import { faMagnifyingGlass } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useState } from "react"; import { useEffect, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
@ -11,11 +11,13 @@ type Props = {
export default function SearchBar({ placeholder }: Props) { export default function SearchBar({ placeholder }: Props) {
const router = useRouter(); const router = useRouter();
const routeQuery = router.query.q; const [searchQuery, setSearchQuery] = useState("");
const [searchQuery, setSearchQuery] = useState( useEffect(() => {
routeQuery ? decodeURIComponent(routeQuery as string) : "" router.query.q
); ? setSearchQuery(decodeURIComponent(router.query.q as string))
: setSearchQuery("");
}, [router.query.q]);
return ( return (
<div className="flex items-center relative group"> <div className="flex items-center relative group">

View File

@ -44,7 +44,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/settings/account` active === `/settings/account`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`} } duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
> >
@ -58,7 +58,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/settings/appearance` active === `/settings/appearance`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`} } duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
> >
@ -75,7 +75,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/settings/archive` active === `/settings/archive`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`} } duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
> >
@ -92,7 +92,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/settings/api` active === `/settings/api`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`} } duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
> >
@ -106,7 +106,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/settings/password` active === `/settings/password`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`} } duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
> >
@ -121,7 +121,7 @@ export default function SettingsSidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/settings/billing` active === `/settings/billing`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`} } duration-100 py-2 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
> >

View File

@ -60,9 +60,7 @@ export default function Sidebar({ className }: { className?: string }) {
<Link href={`/dashboard`}> <Link href={`/dashboard`}>
<div <div
className={`${ className={`${
active === `/dashboard` active === `/dashboard` ? "bg-primary/20" : "hover:bg-neutral/20"
? "bg-secondary/30"
: "hover:bg-neutral/20"
} duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`} } duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
> >
<FontAwesomeIcon <FontAwesomeIcon
@ -76,7 +74,7 @@ export default function Sidebar({ className }: { className?: string }) {
<Link href={`/links`}> <Link href={`/links`}>
<div <div
className={`${ className={`${
active === `/links` ? "bg-secondary/30" : "hover:bg-neutral/20" active === `/links` ? "bg-primary/20" : "hover:bg-neutral/20"
} duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`} } duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
> >
<FontAwesomeIcon <FontAwesomeIcon
@ -91,7 +89,7 @@ export default function Sidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/collections` active === `/collections`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`} } duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
> >
@ -107,7 +105,7 @@ export default function Sidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/links/pinned` active === `/links/pinned`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`} } duration-100 py-5 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
> >
@ -154,7 +152,7 @@ export default function Sidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/collections/${e.id}` active === `/collections/${e.id}`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`} } duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8 capitalize`}
> >
@ -222,7 +220,7 @@ export default function Sidebar({ className }: { className?: string }) {
<div <div
className={`${ className={`${
active === `/tags/${e.id}` active === `/tags/${e.id}`
? "bg-secondary/30" ? "bg-primary/20"
: "hover:bg-neutral/20" : "hover:bg-neutral/20"
} duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`} } duration-100 py-1 px-2 cursor-pointer flex items-center gap-2 w-full rounded-md h-8`}
> >

View File

@ -21,17 +21,15 @@ export default function SubmitButton({
return ( return (
<button <button
type={type ? type : undefined} type={type ? type : undefined}
className={`text-white flex items-center gap-2 py-2 px-5 rounded-md text-lg tracking-wide select-none font-semibold duration-100 w-fit ${ className={`btn btn-primary text-white tracking-wider w-fit flex items-center gap-2 ${
loading className || ""
? "bg-sky-600 cursor-auto" }`}
: "bg-sky-700 hover:bg-sky-600 cursor-pointer"
} ${className || ""}`}
onClick={() => { onClick={() => {
if (!loading && onClick) onClick(); if (!loading && onClick) onClick();
}} }}
> >
{icon && <FontAwesomeIcon icon={icon} className="h-5" />} {icon && <FontAwesomeIcon icon={icon} className="h-5" />}
<p className="text-center w-full">{label}</p> <p>{label}</p>
</button> </button>
); );
} }

View File

@ -9,7 +9,6 @@ import { useEffect, useState } from "react";
import MainLayout from "@/layouts/MainLayout"; import MainLayout from "@/layouts/MainLayout";
import ProfilePhoto from "@/components/ProfilePhoto"; import ProfilePhoto from "@/components/ProfilePhoto";
import SortDropdown from "@/components/SortDropdown"; import SortDropdown from "@/components/SortDropdown";
import useModalStore from "@/store/modals";
import useLinks from "@/hooks/useLinks"; import useLinks from "@/hooks/useLinks";
import usePermissions from "@/hooks/usePermissions"; import usePermissions from "@/hooks/usePermissions";
import NoLinksFound from "@/components/NoLinksFound"; import NoLinksFound from "@/components/NoLinksFound";
@ -18,10 +17,9 @@ import useAccountStore from "@/store/account";
import getPublicUserData from "@/lib/client/getPublicUserData"; import getPublicUserData from "@/lib/client/getPublicUserData";
import EditCollectionModal from "@/components/Modals/EditCollectionModal"; import EditCollectionModal from "@/components/Modals/EditCollectionModal";
import EditCollectionSharingModal from "@/components/Modals/EditCollectionSharingModal"; import EditCollectionSharingModal from "@/components/Modals/EditCollectionSharingModal";
import DeleteCollectionModal from "@/components/Modals/DeleteCollectionModal";
export default function Index() { export default function Index() {
const { setModal } = useModalStore();
const { settings } = useLocalSettingsStore(); const { settings } = useLocalSettingsStore();
const router = useRouter(); const router = useRouter();
@ -76,6 +74,7 @@ export default function Index() {
const [editCollectionModal, setEditCollectionModal] = useState(false); const [editCollectionModal, setEditCollectionModal] = useState(false);
const [editCollectionSharingModal, setEditCollectionSharingModal] = const [editCollectionSharingModal, setEditCollectionSharingModal] =
useState(false); useState(false);
const [deleteCollectionModal, setDeleteCollectionModal] = useState(false);
return ( return (
<MainLayout> <MainLayout>
@ -206,15 +205,7 @@ export default function Index() {
tabIndex={0} tabIndex={0}
onClick={() => { onClick={() => {
(document?.activeElement as HTMLElement)?.blur(); (document?.activeElement as HTMLElement)?.blur();
activeCollection && setDeleteCollectionModal(true);
setModal({
modal: "COLLECTION",
state: true,
method: "UPDATE",
isOwner: permissions === true,
active: activeCollection,
defaultIndex: permissions === true ? 2 : 1,
});
}} }}
> >
{permissions === true {permissions === true
@ -254,6 +245,12 @@ export default function Index() {
modalId={"edit-collection-sharing-modal" + activeCollection.id} modalId={"edit-collection-sharing-modal" + activeCollection.id}
activeCollection={activeCollection} activeCollection={activeCollection}
/> />
<DeleteCollectionModal
isOpen={deleteCollectionModal}
onClose={() => setDeleteCollectionModal(false)}
modalId={"delete-collection-modal" + activeCollection.id}
activeCollection={activeCollection}
/>
</> </>
) : undefined} ) : undefined}
</MainLayout> </MainLayout>

View File

@ -9,7 +9,6 @@ import CollectionCard from "@/components/CollectionCard";
import { useState } from "react"; import { useState } from "react";
import MainLayout from "@/layouts/MainLayout"; import MainLayout from "@/layouts/MainLayout";
import { useSession } from "next-auth/react"; import { useSession } from "next-auth/react";
import useModalStore from "@/store/modals";
import SortDropdown from "@/components/SortDropdown"; import SortDropdown from "@/components/SortDropdown";
import { Sort } from "@/types/global"; import { Sort } from "@/types/global";
import useSort from "@/hooks/useSort"; import useSort from "@/hooks/useSort";
@ -23,8 +22,6 @@ export default function Collections() {
const { data } = useSession(); const { data } = useSession();
const { setModal } = useModalStore();
useSort({ sortBy, setData: setSortedCollections, data: collections }); useSort({ sortBy, setData: setSortedCollections, data: collections });
const [newCollectionModal, setNewCollectionModal] = useState(false); const [newCollectionModal, setNewCollectionModal] = useState(false);
@ -68,11 +65,7 @@ export default function Collections() {
tabIndex={0} tabIndex={0}
onClick={() => { onClick={() => {
(document?.activeElement as HTMLElement)?.blur(); (document?.activeElement as HTMLElement)?.blur();
setModal({ setNewCollectionModal(true);
modal: "COLLECTION",
state: true,
method: "CREATE",
});
}} }}
> >
New Collection New Collection

View File

@ -24,6 +24,7 @@ import useModalStore from "@/store/modals";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { MigrationFormat, MigrationRequest } from "@/types/global"; import { MigrationFormat, MigrationRequest } from "@/types/global";
import DashboardItem from "@/components/DashboardItem"; import DashboardItem from "@/components/DashboardItem";
import NewLinkModal from "@/components/Modals/NewLinkModal";
export default function Dashboard() { export default function Dashboard() {
const { collections } = useCollectionStore(); const { collections } = useCollectionStore();
@ -99,6 +100,8 @@ export default function Dashboard() {
} }
}; };
const [newLinkModal, setNewLinkModal] = useState(false);
return ( return (
<MainLayout> <MainLayout>
<div style={{ flex: "1 1 auto" }} className="p-5 flex flex-col gap-5"> <div style={{ flex: "1 1 auto" }} className="p-5 flex flex-col gap-5">
@ -187,11 +190,7 @@ export default function Dashboard() {
<div className="text-center w-full mt-4 flex flex-wrap gap-4 justify-center"> <div className="text-center w-full mt-4 flex flex-wrap gap-4 justify-center">
<div <div
onClick={() => { onClick={() => {
setModal({ setNewLinkModal(true);
modal: "LINK",
state: true,
method: "CREATE",
});
}} }}
className="inline-flex gap-1 relative w-[11rem] items-center btn btn-accent text-white group" className="inline-flex gap-1 relative w-[11rem] items-center btn btn-accent text-white group"
> >
@ -200,24 +199,28 @@ export default function Dashboard() {
className="w-5 h-5 left-4 group-hover:ml-[4rem] absolute duration-100" className="w-5 h-5 left-4 group-hover:ml-[4rem] absolute duration-100"
/> />
<span className="group-hover:opacity-0 text-right w-full duration-100"> <span className="group-hover:opacity-0 text-right w-full duration-100">
Create New Item Create New Link
</span> </span>
</div> </div>
<details className="dropdown"> <div className="dropdown dropdown-bottom">
<summary className="flex gap-2 text-sm btn btn-outline group"> <div
tabIndex={0}
role="button"
className="flex gap-2 text-sm btn btn-outline btn-neutral group"
id="import-dropdown"
>
<FontAwesomeIcon <FontAwesomeIcon
icon={faFileImport} icon={faFileImport}
className="w-5 h-5 duration-100" className="w-5 h-5 duration-100"
id="import-dropdown"
/> />
<span className="duration-100" id="import-dropdown"> <p>Import From</p>
Import Your Bookmarks </div>
</span>
</summary>
<ul className="shadow menu dropdown-content z-[1] p-1 bg-base-200 border border-neutral-content rounded-xl mt-1 w-60"> <ul className="shadow menu dropdown-content z-[1] p-1 bg-base-200 border border-neutral-content rounded-xl mt-1 w-60">
<li> <li>
<label <label
tabIndex={0}
role="button"
className="px-2 py-1 rounded-lg" className="px-2 py-1 rounded-lg"
htmlFor="import-linkwarden-file" htmlFor="import-linkwarden-file"
title="JSON File" title="JSON File"
@ -237,6 +240,8 @@ export default function Dashboard() {
</li> </li>
<li> <li>
<label <label
tabIndex={0}
role="button"
className="px-2 py-1 rounded-lg" className="px-2 py-1 rounded-lg"
htmlFor="import-html-file" htmlFor="import-html-file"
title="HTML File" title="HTML File"
@ -255,7 +260,7 @@ export default function Dashboard() {
</label> </label>
</li> </li>
</ul> </ul>
</details> </div>
</div> </div>
</div> </div>
)} )}
@ -288,7 +293,6 @@ export default function Dashboard() {
className={`grid overflow-hidden 2xl:grid-cols-3 xl:grid-cols-2 grid-cols-1 gap-5 w-full`} className={`grid overflow-hidden 2xl:grid-cols-3 xl:grid-cols-2 grid-cols-1 gap-5 w-full`}
> >
{links {links
.filter((e) => e.pinnedBy && e.pinnedBy[0]) .filter((e) => e.pinnedBy && e.pinnedBy[0])
.map((e, i) => <LinkCard key={i} link={e} count={i} />) .map((e, i) => <LinkCard key={i} link={e} count={i} />)
.slice(0, showLinks)} .slice(0, showLinks)}
@ -311,6 +315,11 @@ export default function Dashboard() {
)} )}
</div> </div>
</div> </div>
<NewLinkModal
isOpen={newLinkModal}
onClose={() => setNewLinkModal(false)}
modalId="new-link-modal"
/>
</MainLayout> </MainLayout>
); );
} }

View File

@ -239,7 +239,7 @@ export default function Index() {
<Link key={i} href={`/tags/${e.id}`} className="z-10"> <Link key={i} href={`/tags/${e.id}`} className="z-10">
<p <p
title={e.name} title={e.name}
className="px-2 bg-sky-200 dark:bg-sky-900 text-xs rounded-3xl cursor-pointer hover:opacity-60 duration-100 truncate max-w-[19rem]" className="btn btn-xs btn-outline truncate max-w-[19rem]"
> >
{e.name} {e.name}
</p> </p>

View File

@ -9,7 +9,6 @@ import Head from "next/head";
import useLinks from "@/hooks/useLinks"; import useLinks from "@/hooks/useLinks";
import useLinkStore from "@/store/links"; import useLinkStore from "@/store/links";
import ProfilePhoto from "@/components/ProfilePhoto"; import ProfilePhoto from "@/components/ProfilePhoto";
import useModalStore from "@/store/modals";
import ModalManagement from "@/components/ModalManagement"; import ModalManagement from "@/components/ModalManagement";
import ToggleDarkMode from "@/components/ToggleDarkMode"; import ToggleDarkMode from "@/components/ToggleDarkMode";
import getPublicUserData from "@/lib/client/getPublicUserData"; import getPublicUserData from "@/lib/client/getPublicUserData";
@ -37,16 +36,9 @@ const cardVariants: Variants = {
export default function PublicCollections() { export default function PublicCollections() {
const { links } = useLinkStore(); const { links } = useLinkStore();
const { modal, setModal } = useModalStore();
const { settings } = useLocalSettingsStore(); const { settings } = useLocalSettingsStore();
useEffect(() => {
modal
? (document.body.style.overflow = "hidden")
: (document.body.style.overflow = "auto");
}, [modal]);
const router = useRouter(); const router = useRouter();
const [collectionOwner, setCollectionOwner] = useState({ const [collectionOwner, setCollectionOwner] = useState({

View File

@ -236,10 +236,14 @@ export default function Index() {
</p> </p>
</Link> </Link>
{link?.tags.map((e, i) => ( {link?.tags.map((e, i) => (
<Link key={i} href={`/tags/${e.id}`} className="z-10"> <Link
key={i}
href={"/public/collections/20?q=" + e.name}
className="z-10"
>
<p <p
title={e.name} title={e.name}
className="px-2 py-1 bg-sky-200 dark:bg-sky-900 text-xs rounded-3xl cursor-pointer hover:opacity-60 duration-100 truncate max-w-[19rem]" className="btn btn-xs btn-outline truncate max-w-[19rem]"
> >
{e.name} {e.name}
</p> </p>

View File

@ -155,7 +155,7 @@ export default function Account() {
<div className="divider my-3"></div> <div className="divider my-3"></div>
<div className="flex flex-col gap-10"> <div className="flex flex-col gap-5">
<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>
@ -215,7 +215,7 @@ export default function Account() {
</div> </div>
)} )}
<div className="absolute -bottom-3 left-0 right-0 mx-auto w-fit text-center"> <div className="absolute -bottom-3 left-0 right-0 mx-auto w-fit text-center">
<label className="btn btn-xs btn-secondary btn-outline bg-base-100"> <label className="btn btn-xs btn-neutral btn-outline bg-base-100">
Browse... Browse...
<input <input
type="file" type="file"
@ -243,16 +243,20 @@ export default function Account() {
<div className="flex gap-3 flex-col"> <div className="flex gap-3 flex-col">
<div> <div>
<p className="mb-2">Import your data from other platforms.</p> <p className="mb-2">Import your data from other platforms.</p>
<details className="dropdown"> <div className="dropdown dropdown-bottom">
<summary <div
className="flex gap-2 text-sm btn btn-outline btn-secondary btn-xs" tabIndex={0}
role="button"
className="flex gap-2 text-sm btn btn-outline btn-neutral btn-xs"
id="import-dropdown" id="import-dropdown"
> >
Import From Import From
</summary> </div>
<ul className="shadow menu dropdown-content z-[1] p-1 bg-base-200 border border-neutral-content rounded-xl mt-1 w-60"> <ul className="shadow menu dropdown-content z-[1] p-1 bg-base-200 border border-neutral-content rounded-xl mt-1 w-60">
<li> <li>
<label <label
tabIndex={0}
role="button"
className="px-2 py-1 rounded-lg" className="px-2 py-1 rounded-lg"
htmlFor="import-linkwarden-file" htmlFor="import-linkwarden-file"
title="JSON File" title="JSON File"
@ -272,6 +276,8 @@ export default function Account() {
</li> </li>
<li> <li>
<label <label
tabIndex={0}
role="button"
className="px-2 py-1 rounded-lg" className="px-2 py-1 rounded-lg"
htmlFor="import-html-file" htmlFor="import-html-file"
title="HTML File" title="HTML File"
@ -290,13 +296,13 @@ export default function Account() {
</label> </label>
</li> </li>
</ul> </ul>
</details> </div>
</div> </div>
<div> <div>
<p className="mb-2">Download your data instantly.</p> <p className="mb-2">Download your data instantly.</p>
<Link className="w-fit" href="/api/v1/migration"> <Link className="w-fit" href="/api/v1/migration">
<div className="btn btn-outline btn-secondary btn-xs"> <div className="btn btn-outline btn-neutral btn-xs">
Export Data Export Data
</div> </div>
</Link> </Link>
@ -365,15 +371,15 @@ export default function Account() {
You will be prompted to enter your password before the deletion You will be prompted to enter your password before the deletion
process. process.
</p> </p>
</div>
<Link <Link
href="/settings/delete" href="/settings/delete"
className="mx-auto lg:mx-0 text-white mt-3 flex items-center gap-2 py-1 px-3 rounded-md text-lg tracking-wide select-none font-semibold duration-100 w-fit bg-red-500 hover:bg-red-400 cursor-pointer" className="mx-auto lg:mx-0 text-white flex items-center gap-2 py-1 px-3 rounded-md text-lg tracking-wide select-none font-semibold duration-100 w-fit bg-red-500 hover:bg-red-400 cursor-pointer"
> >
<p className="text-center w-full">Delete Your Account</p> <p className="text-center w-full">Delete Your Account</p>
</Link> </Link>
</div> </div>
</div>
</SettingsLayout> </SettingsLayout>
); );
} }

View File

@ -67,7 +67,7 @@ export default function Appearance() {
<div className="divider my-3"></div> <div className="divider my-3"></div>
<div className="flex flex-col gap-10"> <div className="flex flex-col gap-5">
<div> <div>
<p className="mb-3">Select Theme</p> <p className="mb-3">Select Theme</p>
<div className="flex gap-3 w-full"> <div className="flex gap-3 w-full">

View File

@ -6,8 +6,8 @@ module.exports = {
themes: [ themes: [
{ {
light: { light: {
primary: "#0ea5e9", primary: "#0284c7",
secondary: "#06b6d4", secondary: "#0891b2",
accent: "#6366f1", accent: "#6366f1",
neutral: "#6b7280", neutral: "#6b7280",
"neutral-content": "#d1d5db", "neutral-content": "#d1d5db",
@ -22,8 +22,8 @@ module.exports = {
}, },
{ {
dark: { dark: {
primary: "#0ea5e9", primary: "#38bdf8",
secondary: "#0e7490", secondary: "#22d3ee",
accent: "#6366f1", accent: "#6366f1",
neutral: "#9ca3af", neutral: "#9ca3af",
"neutral-content": "#404040", "neutral-content": "#404040",