improvements

This commit is contained in:
daniel31x13
2025-09-22 08:55:49 -04:00
parent bbbd7a242d
commit 37c1100e37
5 changed files with 145 additions and 78 deletions

View File

@@ -1,5 +1,4 @@
import Link from "next/link";
import React, { useEffect, useState } from "react";
import React, { useState } from "react";
import { useTranslation } from "next-i18next";
import {
DropdownMenu,
@@ -39,16 +38,16 @@ export default function TagCard({
return (
<div
className={cn(
"relative rounded-xl p-2 flex gap-2 flex-col shadow-md cursor-pointer hover:shadow-none hover:bg-opacity-70 duration-200 border border-neutral-content",
"relative rounded-xl p-2 shadow-md cursor-pointer hover:shadow-none hover:bg-opacity-70 duration-200 border border-neutral-content",
editMode ? "bg-base-300" : "bg-base-200",
selected && "border-primary"
)}
onClick={() =>
editMode ? onSelect(tag.id) : router.push(`/tags/${tag.id}`)
}
>
{editMode ? (
<Checkbox checked={selected} className="absolute top-3 right-3 z-20" />
<Checkbox
checked={selected}
className="absolute top-3 right-3 z-20 pointer-events-none"
/>
) : (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -79,25 +78,32 @@ export default function TagCard({
</DropdownMenu>
)}
<h2 className="truncate leading-tight py-1 pr-8" title={tag.name}>
{tag.name}
</h2>
<div
className="flex gap-2 flex-col"
onClick={() =>
editMode ? onSelect(tag.id) : router.push(`/tags/${tag.id}`)
}
>
<h2 className="truncate leading-tight py-1 pr-8" title={tag.name}>
{tag.name}
</h2>
<div className="flex justify-between items-center mt-auto">
<div className="text-xs flex gap-1 items-center">
<i
className="bi-calendar3 text-neutral"
title={t("collection_publicly_shared")}
></i>
{formattedDate}
</div>
<div className="flex justify-between items-center mt-auto">
<div className="text-xs flex gap-1 items-center">
<i
className="bi-calendar3 text-neutral"
title={t("collection_publicly_shared")}
></i>
{formattedDate}
</div>
<div className="text-xs flex gap-1 items-center">
<i
className="bi-link-45deg text-lg leading-none text-neutral"
title={t("collection_publicly_shared")}
></i>
{tag._count?.links}
<div className="text-xs flex gap-1 items-center">
<i
className="bi-link-45deg text-lg leading-none text-neutral"
title={t("collection_publicly_shared")}
></i>
{tag._count?.links}
</div>
</div>
</div>

View File

@@ -9,17 +9,17 @@ import PageHeader from "@/components/PageHeader";
import getServerSideProps from "@/lib/client/getServerSideProps";
import { useTranslation } from "next-i18next";
import { useCollections } from "@linkwarden/router/collections";
import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
} from "@/components/ui/dropdown-menu";
import { Button } from "@/components/ui/button";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
export default function Collections() {
const { t } = useTranslation();
const { data: collections = [] } = useCollections();
const { data: collections = [], isLoading } = useCollections();
const [sortBy, setSortBy] = useState<Sort>(Sort.DateNewestFirst);
const { data } = useSession();
@@ -62,23 +62,22 @@ export default function Collections() {
title={t("collections")}
description={t("collections_you_own")}
/>
<div className="relative">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="text-neutral" variant="ghost" size="icon">
<i className={"bi-three-dots text-neutral text-xl"}></i>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="start">
<DropdownMenuItem
onSelect={() => setNewCollectionModal(true)}
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setNewCollectionModal(true)}
>
<i className="bi-folder"></i>
{t("new_collection")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
<i className="bi-plus-lg text-xl text-neutral"></i>
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p>{t("new_collection")}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div className="flex gap-3 justify-end">
<div className="relative mt-2">
@@ -87,23 +86,45 @@ export default function Collections() {
</div>
</div>
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5">
{sortedCollections
.filter((e) => e.ownerId === data?.user.id && e.parentId === null)
.map((e) => (
<CollectionCard key={e.id} collection={e} />
))}
{!isLoading && collections && !collections[0] ? (
<div
className="card card-compact shadow-md hover:shadow-none duration-200 border border-neutral-content p-5 bg-base-200 self-stretch min-h-[12rem] rounded-xl cursor-pointer flex flex-col gap-4 justify-center items-center group"
onClick={() => setNewCollectionModal(true)}
style={{ flex: "1 1 auto" }}
className="flex flex-col gap-2 justify-center h-full w-full mx-auto p-10"
>
<p className="group-hover:opacity-0 duration-100">
{t("new_collection")}
<p className="text-center text-xl">
{t("create_your_first_collection")}
</p>
<i className="bi-plus-lg text-5xl group-hover:text-7xl group-hover:-mt-10 text-primary drop-shadow duration-100"></i>
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm">
{t("create_your_first_collection_desc")}
</p>
<Button
className="mx-auto mt-5"
variant={"accent"}
onClick={() => setNewCollectionModal(true)}
>
<i className="bi-plus-lg text-xl mr-2" />
{t("new_collection")}
</Button>
</div>
</div>
) : (
<div className="grid 2xl:grid-cols-4 xl:grid-cols-3 sm:grid-cols-2 grid-cols-1 gap-5">
{sortedCollections
.filter((e) => e.ownerId === data?.user.id && e.parentId === null)
.map((e) => (
<CollectionCard key={e.id} collection={e} />
))}
<div
className="card card-compact shadow-md hover:shadow-none duration-200 border border-neutral-content p-5 bg-base-200 self-stretch min-h-[12rem] rounded-xl cursor-pointer flex flex-col gap-4 justify-center items-center group"
onClick={() => setNewCollectionModal(true)}
>
<p className="group-hover:opacity-0 duration-100">
{t("new_collection")}
</p>
<i className="bi-plus-lg text-5xl group-hover:text-7xl group-hover:-mt-10 text-primary drop-shadow duration-100"></i>
</div>
</div>
)}
{sortedCollections.filter((e) => e.ownerId !== data?.user.id)[0] && (
<>

View File

@@ -151,7 +151,7 @@ export default function Index() {
setActiveLink={setActiveLink}
>
<MainLayout>
<div className="p-5 flex flex-col gap-5 w-full">
<div className="p-5 flex flex-col gap-5 w-full h-full">
<LinkListOptions
t={t}
viewMode={viewMode}
@@ -241,6 +241,20 @@ export default function Index() {
placeholderCount={1}
useData={data}
/>
{!data.isLoading && links && !links[0] && (
<div
style={{ flex: "1 1 auto" }}
className="flex flex-col gap-2 justify-center h-full w-full mx-auto p-10"
>
<p className="text-center text-xl">
{t("this_tag_has_no_links")}
</p>
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm">
{t("this_tag_has_no_links_desc")}
</p>
</div>
)}
</div>
{bulkDeleteLinksModal && (
<BulkDeleteLinksModal

View File

@@ -8,7 +8,6 @@ import {
DropdownMenu,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
} from "@/components/ui/dropdown-menu";
@@ -35,7 +34,7 @@ enum TagSort {
export default function Tags() {
const { t } = useTranslation();
const { data: tags = [] } = useTags();
const { data: tags = [], isLoading } = useTags();
const [sortBy, setSortBy] = useState<TagSort>(TagSort.DateNewestFirst);
const [newTagModal, setNewTagModal] = useState(false);
@@ -78,21 +77,22 @@ export default function Tags() {
<div className="flex justify-between items-center">
<div className="flex items-center gap-3">
<PageHeader icon={"bi-hash"} title={t("tags")} />
<div className="relative">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="text-neutral" variant="ghost" size="icon">
<i className={"bi-three-dots text-neutral text-xl"}></i>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => setNewTagModal(true)}
>
<i className="bi-plus-lg text-xl text-neutral"></i>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="bottom" align="start">
<DropdownMenuItem onSelect={() => setNewTagModal(true)}>
<i className="bi-plus-lg" />
{t("new_tag")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</TooltipTrigger>
<TooltipContent side="bottom">
<p>{t("new_tag")}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<div className="flex gap-3 justify-end">
@@ -238,6 +238,26 @@ export default function Tags() {
/>
))}
</div>
{!isLoading && tags && !tags[0] && (
<div
style={{ flex: "1 1 auto" }}
className="flex flex-col gap-2 justify-center h-full w-full mx-auto p-10"
>
<p className="text-center text-xl">{t("create_your_first_tag")}</p>
<p className="text-center mx-auto max-w-96 w-fit text-neutral text-sm">
{t("create_your_first_tag_desc")}
</p>
<Button
className="mx-auto mt-5"
variant={"accent"}
onClick={() => setNewTagModal(true)}
>
<i className="bi-plus-lg text-xl mr-2" />
{t("new_tag")}
</Button>
</div>
)}
</div>
{newTagModal && <NewTagModal onClose={() => setNewTagModal(false)} />}

View File

@@ -514,5 +514,11 @@
"merging": "Merging...",
"delete_tags": "Delete {{count}} Tags",
"tags_deletion_confirmation_message": "Are you sure you want to delete {{count}} Tags? This will remove the tags from all links.",
"subscribe_later": "Subscribe Later?"
"subscribe_later": "Subscribe Later?",
"create_your_first_tag": "Create Your First Tag!",
"create_your_first_tag_desc": "Tags help you categorize and find your Links easily. You can create Tags based on topics, projects, or any system that works for you.",
"create_your_first_collection": "Create Your First Collection!",
"create_your_first_collection_desc": "Collections are like folders for your Links which can then be shared with others.",
"this_tag_has_no_links": "This Tag Has No Links",
"this_tag_has_no_links_desc": "Add this tag to your Links while creating or editing them!"
}