Files
linkwarden/apps/web/pages/dashboard.tsx
Daniel 389e5df117 Chore/tech debts (#1536)
* build(deps): bump the npm_and_yarn group across 5 directories with 22 updates

Bumps the npm_and_yarn group with 18 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [axios](https://github.com/axios/axios) | `1.5.1` | `1.12.0` |
| [dompurify](https://github.com/cure53/DOMPurify) | `3.0.6` | `3.2.4` |
| [formidable](https://github.com/node-formidable/formidable) | `3.5.1` | `3.5.4` |
| [next](https://github.com/vercel/next.js) | `13.4.12` | `14.2.35` |
| [next-auth](https://github.com/nextauthjs/next-auth) | `4.22.1` | `4.24.12` |
| [playwright](https://github.com/microsoft/playwright) | `1.55.0` | `1.55.1` |
| [@mozilla/readability](https://github.com/mozilla/readability) | `0.4.4` | `0.6.0` |
| [ai](https://github.com/vercel/ai) | `4.3.9` | `5.0.52` |
| [nodemailer](https://github.com/nodemailer/nodemailer) | `6.9.3` | `7.0.11` |
| [brace-expansion](https://github.com/juliangruber/brace-expansion) | `1.1.11` | `1.1.12` |
| [braces](https://github.com/micromatch/braces) | `3.0.2` | `3.0.3` |
| [form-data](https://github.com/form-data/form-data) | `3.0.3` | `3.0.4` |
| [js-yaml](https://github.com/nodeca/js-yaml) | `3.14.1` | `3.14.2` |
| [micromatch](https://github.com/micromatch/micromatch) | `4.0.5` | `4.0.8` |
| [min-document](https://github.com/Raynos/min-document) | `2.19.0` | `2.19.2` |
| [nanoid](https://github.com/ai/nanoid) | `3.3.6` | `3.3.8` |
| [node-forge](https://github.com/digitalbazaar/forge) | `1.3.1` | `1.3.3` |
| [tar](https://github.com/isaacs/node-tar) | `6.1.13` | `6.2.1` |

Bumps the npm_and_yarn group with 1 update in the /apps/web directory: [next](https://github.com/vercel/next.js).
Bumps the npm_and_yarn group with 2 updates in the /apps/worker directory: [@mozilla/readability](https://github.com/mozilla/readability) and [ai](https://github.com/vercel/ai).
Bumps the npm_and_yarn group with 1 update in the /packages/lib directory: [nodemailer](https://github.com/nodemailer/nodemailer).
Bumps the npm_and_yarn group with 1 update in the /packages/router directory: [next](https://github.com/vercel/next.js).


Updates `axios` from 1.5.1 to 1.12.0
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.5.1...v1.12.0)

Updates `dompurify` from 3.0.6 to 3.2.4
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.0.6...3.2.4)

Updates `formidable` from 3.5.1 to 3.5.4
- [Release notes](https://github.com/node-formidable/formidable/releases)
- [Changelog](https://github.com/node-formidable/formidable/blob/master/CHANGELOG.md)
- [Commits](https://github.com/node-formidable/formidable/commits)

Updates `next` from 13.4.12 to 14.2.35
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v13.4.12...v14.2.35)

Updates `next-auth` from 4.22.1 to 4.24.12
- [Release notes](https://github.com/nextauthjs/next-auth/releases)
- [Commits](https://github.com/nextauthjs/next-auth/compare/next-auth@4.22.1...next-auth@4.24.12)

Updates `playwright` from 1.55.0 to 1.55.1
- [Release notes](https://github.com/microsoft/playwright/releases)
- [Commits](https://github.com/microsoft/playwright/compare/v1.55.0...v1.55.1)

Updates `postcss` from 8.4.26 to 8.5.3
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.26...8.5.3)

Updates `@mozilla/readability` from 0.4.4 to 0.6.0
- [Changelog](https://github.com/mozilla/readability/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mozilla/readability/compare/0.4.4...0.6.0)

Updates `ai` from 4.3.9 to 5.0.52
- [Release notes](https://github.com/vercel/ai/releases)
- [Changelog](https://github.com/vercel/ai/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vercel/ai/compare/ai@4.3.9...ai@5.0.52)

Updates `nodemailer` from 6.9.3 to 7.0.11
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.3...v7.0.11)

Updates `@babel/runtime` from 7.21.5 to 7.27.0
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.27.0/packages/babel-runtime)

Updates `brace-expansion` from 1.1.11 to 1.1.12
- [Release notes](https://github.com/juliangruber/brace-expansion/releases)
- [Commits](https://github.com/juliangruber/brace-expansion/compare/1.1.11...v1.1.12)

Updates `braces` from 3.0.2 to 3.0.3
- [Changelog](https://github.com/micromatch/braces/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/braces/compare/3.0.2...3.0.3)

Updates `follow-redirects` from 1.15.3 to 1.15.11
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.3...v1.15.11)

Updates `form-data` from 3.0.3 to 3.0.4
- [Release notes](https://github.com/form-data/form-data/releases)
- [Changelog](https://github.com/form-data/form-data/blob/master/CHANGELOG.md)
- [Commits](https://github.com/form-data/form-data/compare/v3.0.3...v3.0.4)

Updates `jose` from 4.14.4 to 4.15.9
- [Release notes](https://github.com/panva/jose/releases)
- [Changelog](https://github.com/panva/jose/blob/v4.15.9/CHANGELOG.md)
- [Commits](https://github.com/panva/jose/compare/v4.14.4...v4.15.9)

Updates `js-yaml` from 3.14.1 to 3.14.2
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

Updates `micromatch` from 4.0.5 to 4.0.8
- [Release notes](https://github.com/micromatch/micromatch/releases)
- [Changelog](https://github.com/micromatch/micromatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/micromatch/compare/4.0.5...4.0.8)

Updates `min-document` from 2.19.0 to 2.19.2
- [Commits](https://github.com/Raynos/min-document/compare/v2.19.0...v2.19.2)

Updates `nanoid` from 3.3.6 to 3.3.8
- [Release notes](https://github.com/ai/nanoid/releases)
- [Changelog](https://github.com/ai/nanoid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ai/nanoid/compare/3.3.6...3.3.8)

Updates `node-forge` from 1.3.1 to 1.3.3
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.1...v1.3.3)

Updates `tar` from 6.1.13 to 6.2.1
- [Release notes](https://github.com/isaacs/node-tar/releases)
- [Changelog](https://github.com/isaacs/node-tar/blob/main/CHANGELOG.md)
- [Commits](https://github.com/isaacs/node-tar/compare/v6.1.13...v6.2.1)

Updates `next` from 13.4.12 to 14.2.35
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v13.4.12...v14.2.35)

Updates `@mozilla/readability` from 0.4.4 to 0.6.0
- [Changelog](https://github.com/mozilla/readability/blob/main/CHANGELOG.md)
- [Commits](https://github.com/mozilla/readability/compare/0.4.4...0.6.0)

Updates `ai` from 4.3.19 to 5.0.113
- [Release notes](https://github.com/vercel/ai/releases)
- [Changelog](https://github.com/vercel/ai/blob/main/CHANGELOG.md)
- [Commits](https://github.com/vercel/ai/compare/ai@4.3.9...ai@5.0.52)

Updates `nodemailer` from 6.10.1 to 7.0.11
- [Release notes](https://github.com/nodemailer/nodemailer/releases)
- [Changelog](https://github.com/nodemailer/nodemailer/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodemailer/nodemailer/compare/v6.9.3...v7.0.11)

Updates `next` from 13.4.12 to 14.2.35
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v13.4.12...v14.2.35)

---
updated-dependencies:
- dependency-name: axios
  dependency-version: 1.12.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: dompurify
  dependency-version: 3.2.4
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: formidable
  dependency-version: 3.5.4
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 14.2.35
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: next-auth
  dependency-version: 4.24.12
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: playwright
  dependency-version: 1.55.1
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: postcss
  dependency-version: 8.5.3
  dependency-type: direct:development
  dependency-group: npm_and_yarn
- dependency-name: "@mozilla/readability"
  dependency-version: 0.6.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: ai
  dependency-version: 5.0.52
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: nodemailer
  dependency-version: 7.0.11
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: "@babel/runtime"
  dependency-version: 7.27.0
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: brace-expansion
  dependency-version: 1.1.12
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: braces
  dependency-version: 3.0.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: follow-redirects
  dependency-version: 1.15.11
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: form-data
  dependency-version: 3.0.4
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: jose
  dependency-version: 4.15.9
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: js-yaml
  dependency-version: 3.14.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: micromatch
  dependency-version: 4.0.8
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: min-document
  dependency-version: 2.19.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: nanoid
  dependency-version: 3.3.8
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: node-forge
  dependency-version: 1.3.3
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: tar
  dependency-version: 6.2.1
  dependency-type: indirect
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 14.2.35
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: "@mozilla/readability"
  dependency-version: 0.6.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: ai
  dependency-version: 5.0.113
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: nodemailer
  dependency-version: 7.0.11
  dependency-type: direct:production
  dependency-group: npm_and_yarn
- dependency-name: next
  dependency-version: 14.2.35
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>

* bug fixes and improvements

* always show navbar in reader view

* bug fix and small performance improvement

* minor fix

* Refactor link selection management and bulk actions

- Replaced the use of selectedLinks with selectedIds in the link store for better performance and clarity.
- Updated LinkListOptions, BulkDeleteLinksModal, and BulkEditLinksModal components to utilize the new selection management.
- Modified LinkCard, LinkMasonry, and LinkList components to handle selection state through props.
- Enhanced updateLinks API to support bulk updates with improved tag management.
- Cleaned up unused imports and code related to previous selection methods.

* move refetching logic to Links component

* move disableDraggable and user hook out of each card to improve efficiency

* cleaner code

* memoize components and increase performance

* fix: update announcement links to use the correct domain

* feat: add favicon field to Link model + update packages + bug fix

* feat: implement favicon fetching API and update Link model for favicon support

* feat: add priority attribute to Image components in Sidebar

* Refactor pages to use consistent layout handling (yes, I forgot to do that until now :P)

* bump version

* Refactor setting pages to use consistent layout handling

* upgrade yarn to 4.12.0

* fix DnD bug

* Enhance announcement handling by adding support for announcement messages

* slimmed down the docker image size

* update Node and yarn versions in playwright tests workflow

* small fix

* fix attempt 2

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-21 18:09:05 -05:00

431 lines
14 KiB
TypeScript

import MainLayout from "@/layouts/MainLayout";
import { ReactElement, useEffect, useMemo, useState } from "react";
import Link from "next/link";
import React from "react";
import { toast } from "react-hot-toast";
import DashboardItem from "@/components/DashboardItem";
import NewLinkModal from "@/components/ModalContent/NewLinkModal";
import PageHeader from "@/components/PageHeader";
import getServerSideProps from "@/lib/client/getServerSideProps";
import { useTranslation } from "next-i18next";
import { useCollections } from "@linkwarden/router/collections";
import { useTags } from "@linkwarden/router/tags";
import { useDashboardData } from "@linkwarden/router/dashboardData";
import { useUpdateUser, useUser } from "@linkwarden/router/user";
import SurveyModal from "@/components/ModalContent/SurveyModal";
import ImportDropdown from "@/components/ImportDropdown";
import { Button } from "@/components/ui/button";
import DashboardLayoutDropdown from "@/components/DashboardLayoutDropdown";
import {
DashboardSection,
DashboardSectionType,
} from "@linkwarden/prisma/client";
import { DashboardLinks } from "@/components/DashboardLinks";
import {
LinkIncludingShortenedCollectionAndTags,
ViewMode,
} from "@linkwarden/types";
import ViewDropdown from "@/components/ViewDropdown";
import clsx from "clsx";
import Icon from "@/components/Icon";
import { DragEndEvent } from "@dnd-kit/core";
import Droppable from "@/components/Droppable";
import { useUpdateLink } from "@linkwarden/router/links";
import usePinLink from "@/lib/client/pinLink";
import { useQueryClient } from "@tanstack/react-query";
import DragNDrop from "@/components/DragNDrop";
import { NextPageWithLayout } from "./_app";
const Page: NextPageWithLayout = () => {
const { t } = useTranslation();
const { data: collections = [] } = useCollections();
const {
data: { links = [], numberOfPinnedLinks, collectionLinks = {} } = {
links: [],
},
...dashboardData
} = useDashboardData();
const { data: tags = [] } = useTags();
const { data: user } = useUser();
const [numberOfLinks, setNumberOfLinks] = useState(0);
const [activeLink, setActiveLink] =
useState<LinkIncludingShortenedCollectionAndTags | null>(null);
const [dashboardSections, setDashboardSections] = useState<
DashboardSection[]
>(user?.dashboardSections || []);
useEffect(() => {
setDashboardSections(user?.dashboardSections || []);
}, [user?.dashboardSections]);
const [viewMode, setViewMode] = useState<ViewMode>(
(localStorage.getItem("viewMode") as ViewMode) || ViewMode.Card
);
useEffect(() => {
setNumberOfLinks(
collections.reduce(
(accumulator, collection) =>
accumulator + (collection._count as any).links,
0
)
);
}, [collections]);
useEffect(() => {
if (
process.env.NEXT_PUBLIC_STRIPE === "true" &&
user &&
user.id &&
user.referredBy === null &&
// if user is using Linkwarden for more than 3 days
new Date().getTime() - new Date(user.createdAt).getTime() >
3 * 24 * 60 * 60 * 1000
) {
setTimeout(() => {
setShowsSurveyModal(true);
}, 1000);
}
}, [user]);
const orderedSections = useMemo(() => {
return [...dashboardSections].sort((a, b) => {
const orderA = a.order ?? Number.MAX_SAFE_INTEGER;
const orderB = b.order ?? Number.MAX_SAFE_INTEGER;
return orderA - orderB;
});
}, [dashboardSections]);
const [newLinkModal, setNewLinkModal] = useState(false);
const [showSurveyModal, setShowsSurveyModal] = useState(false);
const updateUser = useUpdateUser();
const updateLink = useUpdateLink();
const [submitLoader, setSubmitLoader] = useState(false);
// Function to render the dragged item
const submitSurvey = async (referer: string, other?: string) => {
if (submitLoader) return;
setSubmitLoader(true);
const load = toast.loading(t("applying"));
await updateUser.mutateAsync(
{
...user,
referredBy: referer === "other" ? "Other: " + other : referer,
},
{
onSettled: (data, error) => {
console.log(data, error);
setSubmitLoader(false);
toast.dismiss(load);
if (error) {
toast.error(error.message);
} else {
toast.success(t("thanks_for_feedback"));
setShowsSurveyModal(false);
}
},
}
);
};
return (
<>
<div className="p-5 flex flex-col gap-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
<i className="bi-house-fill text-primary" />
<p className="font-thin">{t("dashboard")}</p>
</div>
<div className="flex items-center gap-2">
<DashboardLayoutDropdown />
<ViewDropdown
viewMode={viewMode}
setViewMode={setViewMode}
dashboard
/>
</div>
</div>
{orderedSections[0] ? (
orderedSections?.map((section, i) => (
<Section
key={i}
sectionData={section}
t={t}
collection={collections.find(
(c) => c.id === section.collectionId
)}
collectionLinks={
section.collectionId
? collectionLinks[section.collectionId]
: []
}
links={links}
tags={tags}
numberOfLinks={numberOfLinks}
collectionsLength={collections.length}
numberOfPinnedLinks={numberOfPinnedLinks}
dashboardData={dashboardData}
setNewLinkModal={setNewLinkModal}
/>
))
) : (
<div className="h-full flex flex-col gap-4">
<div className="xl:flex flex flex-col sm:grid grid-cols-2 gap-4 xl:flex-row xl:justify-evenly xl:w-full">
<div className="skeleton h-20 w-full"></div>
<div className="skeleton h-20 w-full"></div>
<div className="skeleton h-20 w-full"></div>
<div className="skeleton h-20 w-full"></div>
</div>
<div className="skeleton h-full"></div>
<div className="skeleton h-full"></div>
<div className="skeleton h-full"></div>
</div>
)}
</div>
{showSurveyModal && (
<SurveyModal
submit={submitSurvey}
onClose={() => {
setShowsSurveyModal(false);
}}
/>
)}
{newLinkModal && <NewLinkModal onClose={() => setNewLinkModal(false)} />}
</>
);
};
Page.getLayout = function getLayout(page: ReactElement) {
return <MainLayout>{page}</MainLayout>;
};
export default Page;
export { getServerSideProps };
type SectionProps = {
sectionData: DashboardSection;
t: (key: string) => string;
collection: any;
collectionsLength: number;
links: any[];
tags: any[];
numberOfLinks: number;
numberOfPinnedLinks: number;
dashboardData: any;
collectionLinks: any[];
setNewLinkModal: (value: boolean) => void;
};
const Section = ({
sectionData,
t,
collection,
links,
tags,
numberOfLinks,
collectionsLength,
numberOfPinnedLinks,
dashboardData,
collectionLinks,
setNewLinkModal,
}: SectionProps) => {
switch (sectionData.type) {
case DashboardSectionType.STATS:
return (
<div className="xl:flex flex flex-col sm:grid grid-cols-2 gap-4 xl:flex-row xl:justify-evenly xl:w-full">
<DashboardItem
name={numberOfLinks === 1 ? t("link") : t("links")}
value={numberOfLinks}
icon={"bi-link-45deg"}
/>
<DashboardItem
name={collectionsLength === 1 ? t("collection") : t("collections")}
value={collectionsLength}
icon={"bi-folder"}
/>
<DashboardItem
name={tags.length === 1 ? t("tag") : t("tags")}
value={tags.length}
icon={"bi-hash"}
/>
<DashboardItem
name={t("pinned")}
value={numberOfPinnedLinks}
icon={"bi-pin-angle"}
/>
</div>
);
case DashboardSectionType.RECENT_LINKS:
return (
<>
<div className="flex justify-between items-center">
<div className="flex gap-2 items-center">
<PageHeader icon={"bi-clock-history"} title={t("recent_links")} />
</div>
<Link
href="/links"
className="flex items-center text-sm text-black/75 dark:text-white/75 gap-2 cursor-pointer"
>
{t("view_all")}
<i className="bi-chevron-right text-sm"></i>
</Link>
</div>
{dashboardData.isLoading ||
(links && links[0] && !dashboardData.isLoading) ? (
<DashboardLinks
type="recent"
links={links}
isLoading={dashboardData.isLoading}
/>
) : (
<div className="flex flex-col gap-2 justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-xl bg-base-200 bg-gradient-to-tr from-neutral-content/70 to-50% to-base-200">
<p className="text-center text-xl">
{t("view_added_links_here")}
</p>
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm mt-2">
{t("view_added_links_here_desc")}
</p>
<div className="text-center w-full mt-4 flex flex-wrap gap-4 justify-center">
<Button
onClick={() => {
setNewLinkModal(true);
}}
variant="accent"
>
<i className="bi-plus-lg text-xl"></i>
{t("add_link")}
</Button>
<ImportDropdown />
</div>
</div>
)}
</>
);
case DashboardSectionType.PINNED_LINKS:
return (
<>
<div className="flex justify-between items-center">
<div className="flex gap-2 items-center">
<PageHeader icon={"bi-pin-angle"} title={t("pinned_links")} />
</div>
<Link
href="/links/pinned"
className="flex items-center text-sm text-black/75 dark:text-white/75 gap-2 cursor-pointer"
>
{t("view_all")}
<i className="bi-chevron-right text-sm "></i>
</Link>
</div>
<Droppable
id="pinned-links-section"
data={{
name: "pinned-links",
}}
>
{dashboardData.isLoading ||
links?.some((e: any) => e.pinnedBy && e.pinnedBy[0]) ? (
<DashboardLinks
links={links.filter((e: any) => e.pinnedBy && e.pinnedBy[0])}
isLoading={dashboardData.isLoading}
/>
) : (
<div className="flex flex-col gap-2 justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-xl bg-base-200 bg-gradient-to-tr from-neutral-content/70 to-50% to-base-200">
<i className="bi-pin mx-auto text-6xl text-primary"></i>
<p className="text-center text-xl">
{t("pin_favorite_links_here")}
</p>
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm">
{t("pin_favorite_links_here_desc")}
</p>
</div>
)}
</Droppable>
</>
);
case DashboardSectionType.COLLECTION:
return (
collection?.id && (
<>
<div className="flex justify-between items-center">
<div className="flex gap-2 items-center">
<div className={clsx("flex items-center gap-3")}>
{collection.icon ? (
<Icon
icon={collection.icon}
color={collection.color || "#0ea5e9"}
className="text-2xl"
/>
) : (
<i
className={`bi-folder-fill text-primary text-2xl drop-shadow`}
style={{ color: collection.color || "#0ea5e9" }}
></i>
)}
<div>
<p className="text-2xl capitalize font-thin">
{collection.name}
</p>
</div>
</div>
</div>
<Link
href={`/collections/${collection.id}`}
className="flex items-center text-sm text-black/75 dark:text-white/75 gap-2 cursor-pointer whitespace-nowrap"
>
{t("view_all")}
<i className="bi-chevron-right text-sm"></i>
</Link>
</div>
<Droppable
id={`dashboard-${collection.id}`}
data={{
id: collection.id,
name: collection.name,
ownerId: collection.ownerId,
type: "collection",
}}
>
{dashboardData.isLoading || collectionLinks?.length > 0 ? (
<DashboardLinks
type="collection"
links={collectionLinks}
isLoading={dashboardData.isLoading}
/>
) : (
<div className="flex flex-col gap-2 justify-center h-full border border-solid border-neutral-content w-full mx-auto p-10 rounded-xl bg-base-200 bg-gradient-to-tr from-neutral-content/70 to-50% to-base-200 min-h-72">
<i className="bi-folder mx-auto text-6xl text-primary"></i>
<p className="text-center text-xl">
{t("no_link_in_collection")}
</p>
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm">
{t("no_link_in_collection_desc")}
</p>
</div>
)}
</Droppable>
</>
)
);
default:
return null;
}
};