From 0a070deebd53c55593e06adadb5e128780273062 Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Fri, 9 Feb 2024 23:24:22 -0600 Subject: [PATCH 01/38] Added selectedLinks to store & checkbox on list view --- components/LinkViews/LinkList.tsx | 14 ++++++++++++-- store/links.ts | 4 ++++ store/localSettings.ts | 1 - types/global.ts | 2 +- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/components/LinkViews/LinkList.tsx b/components/LinkViews/LinkList.tsx index 0ab489a..7d25c46 100644 --- a/components/LinkViews/LinkList.tsx +++ b/components/LinkViews/LinkList.tsx @@ -26,7 +26,11 @@ export default function LinkCardCompact({ flipDropdown, }: Props) { const { collections } = useCollectionStore(); - const { links } = useLinkStore(); + const { links, setSelectedLinks, selectedLinks } = useLinkStore(); + + const handleCheckboxClick = (checkboxId: number) => { + setSelectedLinks((selectedLinks.includes(checkboxId) ? selectedLinks.filter((id) => id !== checkboxId) : [...selectedLinks, checkboxId])); + }; let shortendURL; @@ -56,9 +60,15 @@ export default function LinkCardCompact({ return ( <>
+ handleCheckboxClick(link.id)} + /> void; + setSelectedLinks: (linkIds: number[]) => void; addLink: ( body: LinkIncludingShortenedCollectionAndTags ) => Promise; @@ -27,6 +29,7 @@ type LinkStore = { const useLinkStore = create()((set) => ({ links: [], + selectedLinks: [], setLinks: async (data, isInitialCall) => { isInitialCall && set(() => ({ @@ -45,6 +48,7 @@ const useLinkStore = create()((set) => ({ ), })); }, + setSelectedLinks: (linkIds) => set({ selectedLinks: linkIds }), addLink: async (body) => { const response = await fetch("/api/v1/links", { body: JSON.stringify(body), diff --git a/store/localSettings.ts b/store/localSettings.ts index e38bae8..6c79d6b 100644 --- a/store/localSettings.ts +++ b/store/localSettings.ts @@ -1,5 +1,4 @@ import { create } from "zustand"; -import { ViewMode } from "@/types/global"; type LocalSettings = { theme?: string; diff --git a/types/global.ts b/types/global.ts index 3c8de79..717d046 100644 --- a/types/global.ts +++ b/types/global.ts @@ -9,7 +9,7 @@ export interface LinkIncludingShortenedCollectionAndTags Link, "id" | "createdAt" | "collectionId" | "updatedAt" | "lastPreserved" > { - id?: number; + id: number; createdAt?: string; collectionId?: number; tags: Tag[]; From b51355b406cd272cfe863b198099b4a62bb86cb3 Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Fri, 9 Feb 2024 23:43:23 -0600 Subject: [PATCH 02/38] Check all and display actions --- pages/collections/[id].tsx | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index fd0f970..e239712 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -32,7 +32,7 @@ export default function Index() { const router = useRouter(); - const { links } = useLinkStore(); + const { links, selectedLinks, setSelectedLinks } = useLinkStore(); const { collections } = useCollectionStore(); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); @@ -102,14 +102,21 @@ export default function Index() { // @ts-ignore const LinkComponent = linkView[viewMode]; + const handleSelectAll = () => { + if (selectedLinks.length === links.length) { + setSelectedLinks([]); + } else { + setSelectedLinks(links.map((e) => e.id)); + } + }; + return (
{activeCollection && ( @@ -279,6 +286,29 @@ export default function Index() {
+
+
+ handleSelectAll()} + checked={selectedLinks.length === links.length} + /> + {selectedLinks.length > 0 && + {selectedLinks.length} link selected + } +
+ {selectedLinks.length > 0 && permissions && +
+ + +
+ } +
{links.some((e) => e.collectionId === Number(router.query.id)) ? ( Date: Fri, 9 Feb 2024 23:51:02 -0600 Subject: [PATCH 03/38] Display checkbox on card & reset when collection is changed --- components/LinkViews/LinkCard.tsx | 14 ++++++++++++-- pages/collections/[id].tsx | 12 ++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/components/LinkViews/LinkCard.tsx b/components/LinkViews/LinkCard.tsx index 20694b3..81fbfae 100644 --- a/components/LinkViews/LinkCard.tsx +++ b/components/LinkViews/LinkCard.tsx @@ -24,13 +24,17 @@ type Props = { flipDropdown?: boolean; }; -export default function LinkGrid({ +export default function LinkCard({ link, flipDropdown, }: Props) { const { collections } = useCollectionStore(); - const { links, getLink } = useLinkStore(); + const { links, getLink, setSelectedLinks, selectedLinks } = useLinkStore(); + + const handleCheckboxClick = (checkboxId: number) => { + setSelectedLinks((selectedLinks.includes(checkboxId) ? selectedLinks.filter((id) => id !== checkboxId) : [...selectedLinks, checkboxId])); + }; let shortendURL; @@ -85,6 +89,12 @@ export default function LinkGrid({ ref={ref} className="border border-solid border-neutral-content bg-base-200 shadow-md hover:shadow-none duration-100 rounded-2xl relative" > + handleCheckboxClick(link.id)} + /> handleSelectAll()} checked={selectedLinks.length === links.length} /> - {selectedLinks.length > 0 && - {selectedLinks.length} link selected - } + {selectedLinks.length > 0 && ( + + {selectedLinks.length} {selectedLinks.length === 1 ? 'link' : 'links'} selected + + )} {selectedLinks.length > 0 && permissions &&
From 193c66123be9e41efdeafedaa479a64966fe4f0f Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Fri, 9 Feb 2024 23:56:36 -0600 Subject: [PATCH 04/38] Don't show checkboxes on dashboard --- components/LinkViews/LinkCard.tsx | 16 ++++++++++------ components/LinkViews/LinkList.tsx | 16 ++++++++++------ pages/collections/[id].tsx | 1 + pages/dashboard.tsx | 3 ++- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/components/LinkViews/LinkCard.tsx b/components/LinkViews/LinkCard.tsx index 81fbfae..d6d52ee 100644 --- a/components/LinkViews/LinkCard.tsx +++ b/components/LinkViews/LinkCard.tsx @@ -22,11 +22,13 @@ type Props = { count: number; className?: string; flipDropdown?: boolean; + showCheckbox?: boolean; }; export default function LinkCard({ link, flipDropdown, + showCheckbox, }: Props) { const { collections } = useCollectionStore(); @@ -89,12 +91,14 @@ export default function LinkCard({ ref={ref} className="border border-solid border-neutral-content bg-base-200 shadow-md hover:shadow-none duration-100 rounded-2xl relative" > - handleCheckboxClick(link.id)} - /> + {showCheckbox && + handleCheckboxClick(link.id)} + /> + } - handleCheckboxClick(link.id)} - /> + {showCheckbox && + handleCheckboxClick(link.id)} + /> + }
+
{links[0] ? (
- +
) : (
e.pinnedBy && e.pinnedBy[0]) ? (
e.pinnedBy && e.pinnedBy[0]) .slice(0, showLinks)} From ea31eb47aed7d5efb0a5f9ce4421ad79e8c4f628 Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Sat, 10 Feb 2024 00:37:48 -0600 Subject: [PATCH 05/38] Finished bulk delete links --- components/LinkViews/Layouts/CardView.tsx | 3 + components/LinkViews/Layouts/ListView.tsx | 3 + components/LinkViews/LinkCard.tsx | 3 +- components/LinkViews/LinkList.tsx | 2 +- .../ModalContent/BulkDeleteLinksModal.tsx | 65 +++++++++++++++++++ components/ModalContent/DeleteLinkModal.tsx | 1 - components/ModalContent/NewLinkModal.tsx | 6 +- .../controllers/links/bulk/deleteLinksById.ts | 48 ++++++++++++++ .../links/linkId/deleteLinkById.ts | 2 +- pages/api/v1/links/bulk/index.ts | 15 +++++ pages/collections/[id].tsx | 45 +++++++++---- store/links.ts | 22 +++++++ 12 files changed, 195 insertions(+), 20 deletions(-) create mode 100644 components/ModalContent/BulkDeleteLinksModal.tsx create mode 100644 lib/api/controllers/links/bulk/deleteLinksById.ts create mode 100644 pages/api/v1/links/bulk/index.ts diff --git a/components/LinkViews/Layouts/CardView.tsx b/components/LinkViews/Layouts/CardView.tsx index 3e341cc..cb8c5af 100644 --- a/components/LinkViews/Layouts/CardView.tsx +++ b/components/LinkViews/Layouts/CardView.tsx @@ -3,8 +3,10 @@ import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; export default function CardView({ links, + showCheckbox = true, }: { links: LinkIncludingShortenedCollectionAndTags[]; + showCheckbox?: boolean; }) { return (
@@ -15,6 +17,7 @@ export default function CardView({ link={e} count={i} flipDropdown={i === links.length - 1} + showCheckbox={showCheckbox} /> ); })} diff --git a/components/LinkViews/Layouts/ListView.tsx b/components/LinkViews/Layouts/ListView.tsx index 1839284..aa42780 100644 --- a/components/LinkViews/Layouts/ListView.tsx +++ b/components/LinkViews/Layouts/ListView.tsx @@ -3,8 +3,10 @@ import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; export default function ListView({ links, + showCheckbox = true }: { links: LinkIncludingShortenedCollectionAndTags[]; + showCheckbox?: boolean; }) { return (
@@ -14,6 +16,7 @@ export default function ListView({ key={i} link={e} count={i} + showCheckbox={showCheckbox} flipDropdown={i === links.length - 1} /> ); diff --git a/components/LinkViews/LinkCard.tsx b/components/LinkViews/LinkCard.tsx index d6d52ee..8e7f932 100644 --- a/components/LinkViews/LinkCard.tsx +++ b/components/LinkViews/LinkCard.tsx @@ -16,6 +16,7 @@ import Link from "next/link"; import LinkIcon from "./LinkComponents/LinkIcon"; import useOnScreen from "@/hooks/useOnScreen"; import { generateLinkHref } from "@/lib/client/generateLinkHref"; +import useAccountStore from "@/store/account"; type Props = { link: LinkIncludingShortenedCollectionAndTags; @@ -28,7 +29,7 @@ type Props = { export default function LinkCard({ link, flipDropdown, - showCheckbox, + showCheckbox = true, }: Props) { const { collections } = useCollectionStore(); diff --git a/components/LinkViews/LinkList.tsx b/components/LinkViews/LinkList.tsx index 27e1859..e5c07b2 100644 --- a/components/LinkViews/LinkList.tsx +++ b/components/LinkViews/LinkList.tsx @@ -25,7 +25,7 @@ type Props = { export default function LinkCardCompact({ link, flipDropdown, - showCheckbox, + showCheckbox = true, }: Props) { const { collections } = useCollectionStore(); const { links, setSelectedLinks, selectedLinks } = useLinkStore(); diff --git a/components/ModalContent/BulkDeleteLinksModal.tsx b/components/ModalContent/BulkDeleteLinksModal.tsx new file mode 100644 index 0000000..69ba026 --- /dev/null +++ b/components/ModalContent/BulkDeleteLinksModal.tsx @@ -0,0 +1,65 @@ +import React from "react"; +import useLinkStore from "@/store/links"; +import toast from "react-hot-toast"; +import Modal from "../Modal"; + +type Props = { + onClose: Function; +}; + +export default function BulkDeleteLinksModal({ onClose }: Props) { + const { selectedLinks, setSelectedLinks, deleteLinksById } = useLinkStore(); + + const deleteLink = async () => { + const load = toast.loading(`Deleting ${selectedLinks.length} Link${selectedLinks.length > 1 ? "s" : ""}...`); + + const response = await deleteLinksById(selectedLinks); + + toast.dismiss(load); + + response.ok && toast.success(`Deleted ${selectedLinks.length} Link${selectedLinks.length > 1 ? "s" : ""}!`); + + setSelectedLinks([]); + onClose(); + }; + + return ( + +

Delete {selectedLinks.length}

+ +
+ +
+ {selectedLinks.length > 1 ? ( +

+ Are you sure you want to delete {selectedLinks.length} links? +

+ ) : ( +

+ Are you sure you want to delete this link? +

+ )} + +
+ + + Warning: This action is irreversible! + +
+ +

+ Hold the Shift key while clicking + 'Delete' to bypass this confirmation in the future. +

+ + +
+
+ ); +} diff --git a/components/ModalContent/DeleteLinkModal.tsx b/components/ModalContent/DeleteLinkModal.tsx index a2b123c..1a3a476 100644 --- a/components/ModalContent/DeleteLinkModal.tsx +++ b/components/ModalContent/DeleteLinkModal.tsx @@ -15,7 +15,6 @@ export default function DeleteLinkModal({ onClose, activeLink }: Props) { useState(activeLink); const { removeLink } = useLinkStore(); - const [submitLoader, setSubmitLoader] = useState(false); const router = useRouter(); diff --git a/components/ModalContent/NewLinkModal.tsx b/components/ModalContent/NewLinkModal.tsx index 20bcef2..3fde214 100644 --- a/components/ModalContent/NewLinkModal.tsx +++ b/components/ModalContent/NewLinkModal.tsx @@ -20,6 +20,7 @@ export default function NewLinkModal({ onClose }: Props) { const { data } = useSession(); const initial = { + id: 0, name: "", url: "", description: "", @@ -196,9 +197,8 @@ export default function NewLinkModal({ onClose }: Props) { {optionsExpanded ? "Hide" : "More"} Options

diff --git a/lib/api/controllers/links/bulk/deleteLinksById.ts b/lib/api/controllers/links/bulk/deleteLinksById.ts new file mode 100644 index 0000000..0bc93b0 --- /dev/null +++ b/lib/api/controllers/links/bulk/deleteLinksById.ts @@ -0,0 +1,48 @@ +import { prisma } from "@/lib/api/db"; +import { UsersAndCollections } from "@prisma/client"; +import getPermission from "@/lib/api/getPermission"; +import removeFile from "@/lib/api/storage/removeFile"; + +export default async function deleteLinksById(userId: number, linkIds: number[]) { + console.log("linkIds: ", linkIds); + if (!linkIds || linkIds.length === 0) { + return { response: "Please choose valid links.", status: 401 }; + } + + const deletedLinks = []; + + for (const linkId of linkIds) { + const collectionIsAccessible = await getPermission({ + userId, + linkId, + }); + + const memberHasAccess = collectionIsAccessible?.members.some( + (e: UsersAndCollections) => e.userId === userId && e.canDelete + ); + + if (!(collectionIsAccessible?.ownerId === userId || memberHasAccess)) { + return { response: "Collection is not accessible.", status: 401 }; + } + + const deletedLink = await prisma.link.delete({ + where: { + id: linkId, + }, + }); + + removeFile({ + filePath: `archives/${collectionIsAccessible?.id}/${linkId}.pdf`, + }); + removeFile({ + filePath: `archives/${collectionIsAccessible?.id}/${linkId}.png`, + }); + removeFile({ + filePath: `archives/${collectionIsAccessible?.id}/${linkId}_readability.json`, + }); + + deletedLinks.push(deletedLink); + } + + return { response: deletedLinks, status: 200 }; +} \ No newline at end of file diff --git a/lib/api/controllers/links/linkId/deleteLinkById.ts b/lib/api/controllers/links/linkId/deleteLinkById.ts index 90adba4..db68ee7 100644 --- a/lib/api/controllers/links/linkId/deleteLinkById.ts +++ b/lib/api/controllers/links/linkId/deleteLinkById.ts @@ -1,5 +1,5 @@ import { prisma } from "@/lib/api/db"; -import { Collection, Link, UsersAndCollections } from "@prisma/client"; +import { Link, UsersAndCollections } from "@prisma/client"; import getPermission from "@/lib/api/getPermission"; import removeFile from "@/lib/api/storage/removeFile"; diff --git a/pages/api/v1/links/bulk/index.ts b/pages/api/v1/links/bulk/index.ts new file mode 100644 index 0000000..8a5583b --- /dev/null +++ b/pages/api/v1/links/bulk/index.ts @@ -0,0 +1,15 @@ +import type { NextApiRequest, NextApiResponse } from "next"; +import verifyUser from "@/lib/api/verifyUser"; +import deleteLinksById from "@/lib/api/controllers/links/bulk/deleteLinksById"; + +export default async function links(req: NextApiRequest, res: NextApiResponse) { + const user = await verifyUser({ req, res }); + if (!user) return; + + if (req.method === "DELETE") { + const deleted = await deleteLinksById(user.id, req.body.linkIds); + return res.status(deleted.status).json({ + response: deleted.response, + }); + } +} diff --git a/pages/collections/[id].tsx b/pages/collections/[id].tsx index ef84a39..c3b2964 100644 --- a/pages/collections/[id].tsx +++ b/pages/collections/[id].tsx @@ -25,13 +25,15 @@ import CardView from "@/components/LinkViews/Layouts/CardView"; import ListView from "@/components/LinkViews/Layouts/ListView"; import { dropdownTriggerer } from "@/lib/client/utils"; import NewCollectionModal from "@/components/ModalContent/NewCollectionModal"; +import BulkDeleteLinksModal from "@/components/ModalContent/BulkDeleteLinksModal"; +import toast from "react-hot-toast"; export default function Index() { const { settings } = useLocalSettingsStore(); const router = useRouter(); - const { links, selectedLinks, setSelectedLinks } = useLinkStore(); + const { links, selectedLinks, setSelectedLinks, deleteLinksById } = useLinkStore(); const { collections } = useCollectionStore(); const [sortBy, setSortBy] = useState(Sort.DateNewestFirst); @@ -90,6 +92,7 @@ export default function Index() { const [editCollectionSharingModal, setEditCollectionSharingModal] = useState(false); const [deleteCollectionModal, setDeleteCollectionModal] = useState(false); + const [bulkDeleteLinksModal, setBulkDeleteLinksModal] = useState(false); const [viewMode, setViewMode] = useState( localStorage.getItem("viewMode") || ViewMode.Card @@ -112,6 +115,16 @@ export default function Index() { } }; + const bulkDeleteLinks = async () => { + const load = toast.loading(`Deleting ${selectedLinks.length} Link${selectedLinks.length > 1 ? "s" : ""}...`); + + const response = await deleteLinksById(selectedLinks); + + toast.dismiss(load); + + response.ok && toast.success(`Deleted ${selectedLinks.length} Link${selectedLinks.length > 1 ? "s" : ""}!`); + }; + return (
handleSelectAll()} - checked={selectedLinks.length === links.length} + checked={selectedLinks.length === links.length && links.length > 0} /> {selectedLinks.length > 0 && ( @@ -308,7 +321,10 @@ export default function Index() { -
@@ -325,34 +341,37 @@ export default function Index() { )}
- {activeCollection ? ( + {activeCollection && ( <> - {editCollectionModal ? ( + {editCollectionModal && ( setEditCollectionModal(false)} activeCollection={activeCollection} /> - ) : undefined} - {editCollectionSharingModal ? ( + )} + {editCollectionSharingModal && ( setEditCollectionSharingModal(false)} activeCollection={activeCollection} /> - ) : undefined} - {newCollectionModal ? ( + )} + {newCollectionModal && ( setNewCollectionModal(false)} parent={activeCollection} /> - ) : undefined} - {deleteCollectionModal ? ( + )} + {deleteCollectionModal && ( setDeleteCollectionModal(false)} activeCollection={activeCollection} /> - ) : undefined} + )} + {bulkDeleteLinksModal && ( + setBulkDeleteLinksModal(false)} /> + )} - ) : undefined} + )} ); } diff --git a/store/links.ts b/store/links.ts index aa55b59..2393c19 100644 --- a/store/links.ts +++ b/store/links.ts @@ -24,6 +24,7 @@ type LinkStore = { link: LinkIncludingShortenedCollectionAndTags ) => Promise; removeLink: (linkId: number) => Promise; + deleteLinksById: (linkIds: number[]) => Promise; resetLinks: () => void; }; @@ -146,6 +147,27 @@ const useLinkStore = create()((set) => ({ return { ok: response.ok, data: data.response }; }, + deleteLinksById: async (linkIds: number[]) => { + const response = await fetch("/api/v1/links/bulk", { + body: JSON.stringify({ linkIds }), + headers: { + "Content-Type": "application/json", + }, + method: "DELETE", + }); + + const data = await response.json(); + + if (response.ok) { + set((state) => ({ + links: state.links.filter((e) => !linkIds.includes(e.id)), + })); + useTagStore.getState().setTags(); + useCollectionStore.getState().setCollections(); + } + + return { ok: response.ok, data: data.response }; + }, resetLinks: () => set({ links: [] }), })); From 26997475fdb46c47927f2f320f6a4286077eb5bb Mon Sep 17 00:00:00 2001 From: Isaac Wise Date: Sat, 10 Feb 2024 01:38:19 -0600 Subject: [PATCH 06/38] Fix permission checking --- components/LinkViews/LinkCard.tsx | 2 +- .../ModalContent/BulkDeleteLinksModal.tsx | 2 +- .../ModalContent/BulkEditLinksModal.tsx | 150 ++++++++++++++++++ .../controllers/links/bulk/deleteLinksById.ts | 33 ++-- .../controllers/links/bulk/updateLinksById.ts | 64 ++++++++ pages/api/v1/links/bulk/index.ts | 9 +- store/links.ts | 24 +++ 7 files changed, 268 insertions(+), 16 deletions(-) create mode 100644 components/ModalContent/BulkEditLinksModal.tsx create mode 100644 lib/api/controllers/links/bulk/updateLinksById.ts diff --git a/components/LinkViews/LinkCard.tsx b/components/LinkViews/LinkCard.tsx index 8e7f932..a85ffcc 100644 --- a/components/LinkViews/LinkCard.tsx +++ b/components/LinkViews/LinkCard.tsx @@ -95,7 +95,7 @@ export default function LinkCard({ {showCheckbox && handleCheckboxClick(link.id)} /> diff --git a/components/ModalContent/BulkDeleteLinksModal.tsx b/components/ModalContent/BulkDeleteLinksModal.tsx index 69ba026..5cb71b7 100644 --- a/components/ModalContent/BulkDeleteLinksModal.tsx +++ b/components/ModalContent/BulkDeleteLinksModal.tsx @@ -25,7 +25,7 @@ export default function BulkDeleteLinksModal({ onClose }: Props) { return ( -

Delete {selectedLinks.length}

+

Delete {selectedLinks.length} Link{selectedLinks.length > 1 ? "s" : ""}!

diff --git a/components/ModalContent/BulkEditLinksModal.tsx b/components/ModalContent/BulkEditLinksModal.tsx new file mode 100644 index 0000000..9aff5fe --- /dev/null +++ b/components/ModalContent/BulkEditLinksModal.tsx @@ -0,0 +1,150 @@ +import React, { useEffect, useState } from "react"; +import CollectionSelection from "@/components/InputSelect/CollectionSelection"; +import TagSelection from "@/components/InputSelect/TagSelection"; +import TextInput from "@/components/TextInput"; +import unescapeString from "@/lib/client/unescapeString"; +import useLinkStore from "@/store/links"; +import { LinkIncludingShortenedCollectionAndTags } from "@/types/global"; +import toast from "react-hot-toast"; +import Link from "next/link"; +import Modal from "../Modal"; + +type Props = { + onClose: Function; +}; + +export default function EditLinkModal({ onClose }: Props) { + const { updateLink, updateLinksById } = useLinkStore(); + const [submitLoader, setSubmitLoader] = useState(false); + const [updatedValues, setUpdatedValues] = useState>(); + + const setCollection = (e: any) => { + if (e?.__isNew__) e.value = null; + + setUpdatedValues({ + ...updatedValues, + collection: { id: e?.value, name: e?.label, ownerId: e?.ownerId }, + }); + }; + + const setTags = (e: any) => { + const tagNames = e.map((e: any) => { + return { name: e.label }; + }); + + setUpdatedValues({ ...updatedValues, tags: tagNames }); + }; + + const submit = async () => { + if (!submitLoader) { + setSubmitLoader(true); + + let response; + + const load = toast.loading("Updating..."); + + response = await updateLinksById(user); + + toast.dismiss(load); + + if (response.ok) { + toast.success(`Updated!`); + onClose(); + } else toast.error(response.data as string); + + setSubmitLoader(false); + + return response; + } + }; + + return ( + +

Edit Link

+ +
+ + {link.url ? ( + + +

{shortendURL}

+ + ) : undefined} + +
+

Name

+ setLink({ ...link, name: e.target.value })} + placeholder="e.g. Example Link" + className="bg-base-200" + /> +
+ +
+ {/*
*/} +
+
+

Collection

+ {link.collection.name ? ( + + ) : null} +
+ +
+

Tags

+ { + return { label: e.name, value: e.id }; + })} + /> +
+ +
+

Description

+